Compare commits
1238 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 | ||
|
|
92e3ea576b | ||
|
|
a0f3b39acc | ||
|
|
d3a5dcce86 | ||
|
|
301462a2de | ||
|
|
e4ea62a53c | ||
|
|
513a063547 | ||
|
|
85ee380708 | ||
|
|
e198f0eee4 | ||
|
|
4bcd5cf611 | ||
|
|
518e995b2c | ||
|
|
e65951c882 | ||
|
|
8841d82ead | ||
|
|
0e0035c886 | ||
|
|
e1c82065a5 | ||
|
|
3b6a3cf3a4 | ||
|
|
bfb530d3a3 | ||
|
|
3ec0f3b8ba | ||
|
|
e81957ee98 | ||
|
|
0c99d8bb60 | ||
|
|
5b5770965e | ||
|
|
043baa7c4b | ||
|
|
12176dbfff | ||
|
|
0240e2b597 | ||
|
|
76135cf8f9 | ||
|
|
6a663f8b1a | ||
|
|
9a95c0aa88 | ||
|
|
4bc1aaaefa | ||
|
|
9378639e4c | ||
|
|
bc2f1a02cc | ||
|
|
c18b5b4e78 | ||
|
|
5b3cf02153 | ||
|
|
6712aa55f7 | ||
|
|
09fef6acc4 | ||
|
|
e3147b0322 | ||
|
|
b0bb0ab1ce | ||
|
|
172d2b8777 | ||
|
|
0483b47e77 | ||
|
|
2a5996f408 | ||
|
|
6e68da0f4d | ||
|
|
32824422a1 | ||
|
|
2b8db5c2c2 | ||
|
|
044b38e632 | ||
|
|
ebdd59b0ae | ||
|
|
c92d69cb09 | ||
|
|
8903b1ea8b | ||
|
|
42bfd4950a | ||
|
|
66090ac3b9 | ||
|
|
ca58da4fa1 | ||
|
|
f6ba8c6c53 | ||
|
|
3a456bb9dd | ||
|
|
a08b38d231 | ||
|
|
ac07f3c460 | ||
|
|
0f12654f4a | ||
|
|
285cfe4c5d | ||
|
|
c0f9169879 | ||
|
|
a586cb311e | ||
|
|
f36e2e72d6 | ||
|
|
eff8f4b222 | ||
|
|
858fd4fb1f | ||
|
|
6f73815043 | ||
|
|
c42a96c987 | ||
|
|
b8c6dcea2e | ||
|
|
deb9f21284 | ||
|
|
1b0fc6b3f3 | ||
|
|
fabb631b7d | ||
|
|
be7b461ae2 | ||
|
|
3eb296c892 | ||
|
|
6ffce31250 | ||
|
|
9bf70ff792 | ||
|
|
f52aa1351c | ||
|
|
43f212dc72 | ||
|
|
bc96351c17 | ||
|
|
de54a17275 | ||
|
|
f542eb48bb | ||
|
|
c793f99e30 | ||
|
|
5391e68668 | ||
|
|
76b5d77505 | ||
|
|
e5cd1bd33e | ||
|
|
8ac075a0aa | ||
|
|
c448680054 | ||
|
|
567a44f28c | ||
|
|
a5a58291e3 | ||
|
|
99c46d6a4e | ||
|
|
d3674f5724 | ||
|
|
0e927d25cf | ||
|
|
ebe4149c39 | ||
|
|
4ea4a809fd | ||
|
|
842eb7c92e | ||
|
|
a23d6a05a6 | ||
|
|
0f9e2466ec | ||
|
|
944ce9a33e | ||
|
|
63408cf8a3 | ||
|
|
c53f3095ec | ||
|
|
df1374dd1a | ||
|
|
6afc988f88 | ||
|
|
fdc1dbfa1d | ||
|
|
cf66cc05fc | ||
|
|
e9d6a2157a | ||
|
|
62ae9dc05c | ||
|
|
2e2d67f315 | ||
|
|
07b2b0d9ea | ||
|
|
2b2c001e7a | ||
|
|
244c74776a | ||
|
|
8b01421c8e | ||
|
|
36954e740d | ||
|
|
260c882b08 | ||
|
|
9084d1eeaf | ||
|
|
82c579eb14 | ||
|
|
58baecc0d2 | ||
|
|
1e875c9e9e | ||
|
|
bb58446f91 | ||
|
|
c40f75d976 | ||
|
|
9dd3df273e | ||
|
|
990a76852e | ||
|
|
c55a6b70ae | ||
|
|
19c9cef9a8 | ||
|
|
6288e6421d | ||
|
|
dd5bcb8509 | ||
|
|
eef9cdb0a7 | ||
|
|
7305ca138c | ||
|
|
c1912fc079 | ||
|
|
dfad09d85c | ||
|
|
5b177ecc4a | ||
|
|
7bb00d195a | ||
|
|
1130ae4f0d | ||
|
|
df44c2a9c6 | ||
|
|
08bfbbe8d4 | ||
|
|
1ffdbf7b0c | ||
|
|
c68838805f | ||
|
|
b88a2ebd08 | ||
|
|
22f14fd300 | ||
|
|
39dae9232f | ||
|
|
810d849dcd | ||
|
|
05f9d2109a | ||
|
|
84e5a00361 | ||
|
|
f1e1ec574f | ||
|
|
17884743bd | ||
|
|
a40ea79277 | ||
|
|
3d169bd80c | ||
|
|
3e80a72883 | ||
|
|
c9e0ad2ac2 | ||
|
|
6005f802ad | ||
|
|
c884378f94 | ||
|
|
755fdd32b5 | ||
|
|
e7e0af2135 | ||
|
|
4a0af66884 | ||
|
|
a54588d45b | ||
|
|
b8f8828eb2 | ||
|
|
ece40abaed | ||
|
|
6005f92566 | ||
|
|
39519dfdc9 | ||
|
|
71e6543430 | ||
|
|
d98eb026c7 | ||
|
|
8754217f6d | ||
|
|
04f63b7c7b | ||
|
|
15f4dad33e | ||
|
|
503eed34b2 | ||
|
|
5503121bea | ||
|
|
4d87e75d71 | ||
|
|
8c5fda2d53 | ||
|
|
c61287ec4f | ||
|
|
6814822cfd | ||
|
|
506c698f53 | ||
|
|
5a9763aaf7 | ||
|
|
f729f89889 | ||
|
|
52da17075e | ||
|
|
7084a380a6 | ||
|
|
957b232413 | ||
|
|
98a52ea808 | ||
|
|
89c6ecafbd | ||
|
|
07ed22df17 | ||
|
|
6b0a16fc6e | ||
|
|
bde317ee69 | ||
|
|
280caad378 | ||
|
|
c612fecfae | ||
|
|
0659928930 | ||
|
|
594bc65793 | ||
|
|
62894e3f84 | ||
|
|
c196b67587 | ||
|
|
aa1fefdc8c | ||
|
|
9d0047f4d6 | ||
|
|
fb31f868f1 | ||
|
|
780a7f5e4a | ||
|
|
111d00617f | ||
|
|
9f0d384ebf | ||
|
|
a8fbc03e26 | ||
|
|
a5ab05eb69 | ||
|
|
a13ab2943a | ||
|
|
8eddb9e737 | ||
|
|
185e5e759b | ||
|
|
8a3e63cdeb | ||
|
|
9cb8f7e941 | ||
|
|
ff80b69183 | ||
|
|
0304a10b2f | ||
|
|
8a54c8a3e3 | ||
|
|
29ebd170ee | ||
|
|
663cbd9ce0 | ||
|
|
6b04092bd8 | ||
|
|
aa078f6868 | ||
|
|
94a07da47b | ||
|
|
a24eff9bf9 | ||
|
|
6eca968bb8 | ||
|
|
29f3518c04 | ||
|
|
33bfe80d95 | ||
|
|
62a174fd06 | ||
|
|
d41e306504 | ||
|
|
4238a630fc | ||
|
|
2d2561024f | ||
|
|
0c8dd59163 | ||
|
|
5365031d9d | ||
|
|
c6af8ba88b | ||
|
|
f81dc50748 | ||
|
|
a0ebdf0399 | ||
|
|
0aa0232f1b | ||
|
|
47821e6b3b | ||
|
|
44e968f7a8 | ||
|
|
6ab08efd6b | ||
|
|
727b8e621e | ||
|
|
1a23f381d3 | ||
|
|
df4ec5b9af | ||
|
|
93bb10f9b4 | ||
|
|
edd6c16637 | ||
|
|
db409ffe7b | ||
|
|
d819945522 | ||
|
|
d316e24df6 | ||
|
|
ab775e7106 | ||
|
|
547f6db6ce | ||
|
|
9f76798e64 | ||
|
|
4db8b725e0 | ||
|
|
ca4e1b3a26 | ||
|
|
24c5e8cb79 | ||
|
|
ae160d66c3 | ||
|
|
94cd593a45 | ||
|
|
0064766ef2 | ||
|
|
b2da71aca0 | ||
|
|
774d5c4f21 | ||
|
|
841830dd7f | ||
|
|
b91b3b1b77 | ||
|
|
56b8eddb22 | ||
|
|
ced8088fe9 | ||
|
|
eeb2d1db6f | ||
|
|
24edf7bdc4 | ||
|
|
996ec49878 | ||
|
|
a4edb0dfc9 | ||
|
|
240b2057f6 | ||
|
|
434a32763c | ||
|
|
107732b111 | ||
|
|
856f0d1881 | ||
|
|
2badf86705 | ||
|
|
6d936680ea | ||
|
|
08a4722ca2 | ||
|
|
04a23f8946 | ||
|
|
976549f440 | ||
|
|
a0e5737a22 | ||
|
|
a642ee40da | ||
|
|
4c34ebe9e2 | ||
|
|
26b929d6da | ||
|
|
ad74108e00 | ||
|
|
ea5a8226fb | ||
|
|
ce3933cf6d | ||
|
|
7866896b70 | ||
|
|
48e6171930 | ||
|
|
2f5ebad9fe | ||
|
|
3bd752eaa0 | ||
|
|
d12a3dbac5 | ||
|
|
c63f40f412 | ||
|
|
4dd9fbdb7b | ||
|
|
32d1ea0d42 | ||
|
|
92a2cc7c31 | ||
|
|
9a894879a7 | ||
|
|
7d5e1de377 | ||
|
|
07957700ea | ||
|
|
e082d47a87 | ||
|
|
4867351e5f | ||
|
|
ecb3c31d21 | ||
|
|
91b9b51426 | ||
|
|
5a8be302b4 | ||
|
|
6c2659f9e4 | ||
|
|
95a1255c3b | ||
|
|
9acb915fb9 | ||
|
|
83e138a5d3 | ||
|
|
3f345f78ba | ||
|
|
bf4efcfd7f | ||
|
|
7fce6e32f2 | ||
|
|
63a9e9e77a | ||
|
|
db62ad5948 | ||
|
|
0c47ceda3a | ||
|
|
422eb21186 | ||
|
|
eb8acaf4d4 | ||
|
|
9d5afbf1f5 | ||
|
|
d214c25b87 | ||
|
|
37e24af753 | ||
|
|
910b71bd9e | ||
|
|
ae455ed8a7 | ||
|
|
ccc953842e | ||
|
|
424b96532f | ||
|
|
2dee0110be | ||
|
|
a2ae6f37d1 | ||
|
|
92bda00296 | ||
|
|
659cdffcba | ||
|
|
bbe1e5df59 | ||
|
|
64556da803 | ||
|
|
846f3b0b50 | ||
|
|
47b987fb66 | ||
|
|
4ba618c875 | ||
|
|
2bd10526a7 | ||
|
|
4baf0b96c6 | ||
|
|
da497c8833 | ||
|
|
544a966b40 | ||
|
|
2f091161b4 | ||
|
|
d6d5e0c86b | ||
|
|
63e498f3e1 | ||
|
|
822497b989 | ||
|
|
18e803075b | ||
|
|
b18d4978b5 | ||
|
|
fedd2ad0bc | ||
|
|
c529d82599 | ||
|
|
c215c6ab52 | ||
|
|
00fac71b68 | ||
|
|
a95f6efb35 | ||
|
|
debe2b8004 | ||
|
|
31e3ba83ff | ||
|
|
9f5e2b59de | ||
|
|
adeb3c67c8 | ||
|
|
2472b6d7a9 | ||
|
|
10ec2647eb | ||
|
|
d4f5afac67 | ||
|
|
211fc0f8a0 | ||
|
|
170d590b67 | ||
|
|
b0126243c0 | ||
|
|
d89bd546e9 | ||
|
|
28ac202f1a | ||
|
|
213dde9304 | ||
|
|
fe1965e322 | ||
|
|
709dae439b | ||
|
|
f1fce08a94 | ||
|
|
31f7b974fd | ||
|
|
37466fd9c7 | ||
|
|
b0382c8fa1 | ||
|
|
24174c840c | ||
|
|
8598e188b1 | ||
|
|
58651f03b6 | ||
|
|
f7831be7ae | ||
|
|
c1b10e7993 | ||
|
|
b1e8c6e818 | ||
|
|
49b2a3bf21 | ||
|
|
c411c57743 | ||
|
|
5a89d11b77 | ||
|
|
8b8eea66ee | ||
|
|
26473baf0e | ||
|
|
74438fcec5 | ||
|
|
996da27f30 | ||
|
|
a86c2ac310 | ||
|
|
6869f8c783 | ||
|
|
89ba8f6e30 | ||
|
|
2371c30542 | ||
|
|
c5f0060d34 | ||
|
|
16f78b1fb2 | ||
|
|
38a82c72e1 | ||
|
|
73fb717618 | ||
|
|
9e969883d4 | ||
|
|
8ffede1f68 | ||
|
|
5c2e978e07 | ||
|
|
7777f0ac6c | ||
|
|
b83f3e87e1 | ||
|
|
660c9e056a | ||
|
|
c0243a9729 | ||
|
|
bce7210b01 | ||
|
|
7f80b23c9d | ||
|
|
3c4dc8df6a | ||
|
|
bd157fe3f0 | ||
|
|
a562863cde | ||
|
|
0d1d64d6e7 | ||
|
|
c7f4bb9c3d | ||
|
|
42230c9b16 | ||
|
|
087bfa570b | ||
|
|
5af8b6387a | ||
|
|
a80f919102 | ||
|
|
55169346f2 | ||
|
|
ce73898b67 | ||
|
|
9052a39558 | ||
|
|
32c9900fda | ||
|
|
144169fd89 | ||
|
|
ca07b7183e | ||
|
|
b346d8b8a9 |
4
.cargo/config.toml
Normal file
4
.cargo/config.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
xfmt = "xtask fmt-packages"
|
||||
qa = "xtask run-example qa-test"
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
CHANGELOG.md merge=union
|
||||
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
|
||||
180
.github/workflows/changelog.yml
vendored
Normal file
180
.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
name: Changelog check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# 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.
|
||||
types: [opened, reopened, labeled, unlabeled, synchronize]
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- 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 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."
|
||||
526
.github/workflows/ci.yml
vendored
526
.github/workflows/ci.yml
vendored
@ -1,15 +1,31 @@
|
||||
# NOTE:
|
||||
#
|
||||
# When adding support for a new chip to `esp-hal`, there are a number of
|
||||
# updates which must be made to the CI workflow in order to reflect this; the
|
||||
# changes are:
|
||||
#
|
||||
# 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' job, add checks as needed for the new chip.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches-ignore:
|
||||
- "gh-readonly-queue/**"
|
||||
- "main"
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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.
|
||||
@ -21,458 +37,162 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
# --------------------------------------------------------------------------
|
||||
# Check
|
||||
# Build Packages
|
||||
|
||||
esp-hal-smartled:
|
||||
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:
|
||||
device: [
|
||||
# RISC-V devices:
|
||||
{ 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:
|
||||
{ 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@v3
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install the Rust toolchain for Xtensa devices:
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
ldproxy: false
|
||||
override: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Check all RISC-V targets:
|
||||
- name: check (esp32c3)
|
||||
run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32c3
|
||||
- name: check (esp32c6)
|
||||
run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32c6
|
||||
- name: check (esp32h2)
|
||||
run: cd esp-hal-smartled/ && cargo +nightly check --features=esp32h2
|
||||
# Check all Xtensa targets:
|
||||
- name: check (esp32)
|
||||
run: cd esp-hal-smartled/ && cargo +esp check --features=esp32,esp32_40mhz
|
||||
- name: check (esp32s2)
|
||||
run: cd esp-hal-smartled/ && cargo +esp check --features=esp32s2
|
||||
- name: check (esp32s3)
|
||||
run: cd esp-hal-smartled/ && cargo +esp check --features=esp32s3
|
||||
|
||||
esp32-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
default: true
|
||||
buildtargets: esp32
|
||||
ldproxy: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
- name: build esp32-hal (no features)
|
||||
run: cd esp32-hal/ && cargo build --examples
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32-hal (common features)
|
||||
run: cd esp32-hal/ && cargo check --examples --features=eh1,ufmt
|
||||
- name: check esp32-hal (async)
|
||||
run: cd esp32-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32-hal (async, gpio)
|
||||
run: cd esp32-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32-hal (async, spi)
|
||||
run: cd esp32-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32-hal (async, serial)
|
||||
run: cd esp32-hal/ && cargo check --example=embassy_serial --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32-hal (async, i2c)
|
||||
run: cd esp32-hal/ && cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async
|
||||
|
||||
esp32c2-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
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
|
||||
toolchain: nightly
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: stable
|
||||
components: rust-src
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
# We also use this as an opportunity to verify that the examples link
|
||||
# for each supported image format.
|
||||
- name: build esp32c2-hal (no features)
|
||||
run: cd esp32c2-hal/ && cargo build --examples
|
||||
- name: build esp32c2-hal (direct-boot)
|
||||
run: cd esp32c2-hal/ && cargo build --examples --features=direct-boot
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32c2-hal (common features)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --examples --features=eh1,ufmt
|
||||
- name: check esp32c2-hal (async, systick)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32c2-hal (async, timg0)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32c2-hal (async, gpio)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_wait --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c2-hal (async, spi)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c2-hal (async, serial)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_serial --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c2-hal (async, i2c)
|
||||
run: cd esp32c2-hal/ && cargo +nightly check --example=embassy_i2c --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c2-hal (interrupt-preemption)
|
||||
run: cd esp32c2-hal/ && cargo check --example=interrupt_preemption --features=interrupt-preemption
|
||||
- name: Build and Check
|
||||
uses: ./.github/actions/check-esp-hal
|
||||
with:
|
||||
device: ${{ matrix.device.soc }}
|
||||
target: ${{ matrix.device.target }}
|
||||
toolchain: ${{ matrix.device.toolchain }}
|
||||
|
||||
esp32c3-hal:
|
||||
extras:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
toolchain: stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
# We also use this as an opportunity to verify that the examples link
|
||||
# for each supported image format.
|
||||
- name: build esp32c3-hal (no features)
|
||||
run: cd esp32c3-hal/ && cargo +nightly build --examples
|
||||
- name: build esp32c3-hal (direct-boot)
|
||||
run: cd esp32c3-hal/ && cargo +nightly build --examples --features=direct-boot
|
||||
- name: build esp32c3-hal (mcu-boot)
|
||||
run: cd esp32c3-hal/ && cargo +nightly build --examples --features=mcu-boot
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32c3-hal (common features)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --examples --features=eh1,ufmt
|
||||
- name: check esp32c3-hal (async, systick)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32c3-hal (async, timg0)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32c3-hal (async, gpio)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_wait --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c3-hal (async, spi)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c3-hal (async, serial)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_serial --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c3-hal (async, i2c)
|
||||
run: cd esp32c3-hal/ && cargo +nightly check --example=embassy_i2c --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c3-hal (interrupt-preemption)
|
||||
run: cd esp32c3-hal/ && cargo check --example=interrupt_preemption --features=interrupt-preemption
|
||||
|
||||
esp32c6-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imac-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
# We also use this as an opportunity to verify that the examples link
|
||||
# for each supported image format.
|
||||
- name: build esp32c6-hal (no features)
|
||||
run: cd esp32c6-hal/ && cargo +nightly build --examples
|
||||
- name: build esp32c6-hal (direct-boot)
|
||||
run: cd esp32c6-hal/ && cargo +nightly build --examples --features=direct-boot
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32c6-hal (common features)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --examples --features=eh1,ufmt
|
||||
- name: check esp32c6-hal (async, systick)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32c6-hal (async, timg0)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32c6-hal (async, gpio)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_wait --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c6-hal (async, spi)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c6-hal (async, serial)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_serial --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c6-hal (async, i2c)
|
||||
run: cd esp32c6-hal/ && cargo +nightly check --example=embassy_i2c --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32c6-hal (interrupt-preemption)
|
||||
run: cd esp32c6-hal/ && cargo check --example=interrupt_preemption --features=interrupt-preemption
|
||||
|
||||
esp32h2-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imac-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
# We also use this as an opportunity to verify that the examples link
|
||||
# for each supported image format.
|
||||
- name: build esp32h2-hal (no features)
|
||||
run: cd esp32h2-hal/ && cargo +nightly build --examples
|
||||
- name: build esp32h2-hal (direct-boot)
|
||||
run: cd esp32h2-hal/ && cargo +nightly build --examples --features=direct-boot
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32h2-hal (common features)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --examples --features=eh1,ufmt
|
||||
- name: check esp32h2-hal (async, systick)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32h2-hal (async, timg0)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32h2-hal (async, gpio)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_wait --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32h2-hal (async, spi)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32h2-hal (async, serial)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_serial --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32h2-hal (async, i2c)
|
||||
run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_i2c --features=embassy,embassy-time-systick,async
|
||||
- name: check esp32h2-hal (interrupt-preemption)
|
||||
run: cd esp32h2-hal/ && cargo check --example=interrupt_preemption --features=interrupt-preemption
|
||||
|
||||
esp32s2-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
default: true
|
||||
buildtargets: esp32s2
|
||||
ldproxy: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
- name: check esp32s2-hal (no features)
|
||||
run: cd esp32s2-hal/ && cargo build --examples
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32s2-hal (common features)
|
||||
run: cd esp32s2-hal/ && cargo check --examples --features=eh1,ufmt
|
||||
# FIXME: `time-systick` feature disabled for now, see 'esp32s2-hal/Cargo.toml'.
|
||||
# - name: check esp32s2-hal (async, systick)
|
||||
# run: cd esp32s2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32s2-hal (async, timg0)
|
||||
run: cd esp32s2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32s2-hal (async, gpio)
|
||||
run: cd esp32s2-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s2-hal (async, spi)
|
||||
run: cd esp32s2-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s2-hal (async, serial)
|
||||
run: cd esp32s2-hal/ && cargo check --example=embassy_serial --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s2-hal (async, i2c)
|
||||
run: cd esp32s2-hal/ && cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async
|
||||
|
||||
esp32s3-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
default: true
|
||||
buildtargets: esp32s3
|
||||
ldproxy: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Perform a full build initially to verify that the examples not only
|
||||
# build, but also link successfully.
|
||||
# We also use this as an opportunity to verify that the examples link
|
||||
# for each supported image format.
|
||||
- name: build esp32s3-hal (no features)
|
||||
run: cd esp32s3-hal/ && cargo build --examples
|
||||
- name: build esp32s3-hal (direct-boot)
|
||||
run: cd esp32s3-hal/ && cargo build --examples --features=direct-boot
|
||||
# Subsequent steps can just check the examples instead, as we're already
|
||||
# confident that they link.
|
||||
- name: check esp32s3-hal (common features)
|
||||
run: cd esp32s3-hal/ && cargo check --examples --features=eh1,ufmt
|
||||
- name: check esp32s3-hal (async, systick)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick
|
||||
- name: check esp32s3-hal (async, timg0)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0
|
||||
- name: check esp32s3-hal (async, gpio)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s3-hal (async, spi)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s3-hal (async, serial)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_serial --features=embassy,embassy-time-timg0,async
|
||||
- name: check esp32s3-hal (async, i2c)
|
||||
run: cd esp32s3-hal/ && cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async
|
||||
|
||||
esp-riscv-rt:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imac-unknown-none-elf, riscv32imc-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Check esp-riscv-rt (imc)
|
||||
run: cd esp-riscv-rt/ && cargo check --target=riscv32imc-unknown-none-elf -Zbuild-std=core
|
||||
- name: Check esp-riscv-rt (imac)
|
||||
run: cd esp-riscv-rt/ && cargo check --target=riscv32imac-unknown-none-elf -Zbuild-std=core
|
||||
- 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@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
ldproxy: false
|
||||
version: ${{ env.MSRV }}
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf, riscv32imac-unknown-none-elf
|
||||
toolchain: "1.65.0"
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: ${{ env.MSRV }}
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Verify the MSRV for all RISC-V chips.
|
||||
- name: msrv (esp32c2-hal)
|
||||
run: cd esp32c2-hal/ && cargo check --features=eh1,ufmt
|
||||
- name: msrv (esp32c3-hal)
|
||||
run: cd esp32c3-hal/ && cargo check --features=eh1,ufmt
|
||||
- name: msrv (esp32c6-hal)
|
||||
run: cd esp32c6-hal/ && cargo check --features=eh1,ufmt
|
||||
- name: msrv (esp32h2-hal)
|
||||
run: cd esp32h2-hal/ && cargo check --features=eh1,ufmt
|
||||
- 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
|
||||
|
||||
msrv-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@v3
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
ldproxy: false
|
||||
version: "1.65.0"
|
||||
- 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
|
||||
|
||||
# Verify the MSRV for all Xtensa chips.
|
||||
- name: msrv (esp32-hal)
|
||||
run: cd esp32-hal/ && cargo check --features=eh1,ufmt
|
||||
- name: msrv (esp32s2-hal)
|
||||
run: cd esp32s2-hal/ && cargo check --features=eh1,ufmt
|
||||
- name: msrv (esp32s3-hal)
|
||||
run: cd esp32s3-hal/ && cargo check --features=eh1,ufmt
|
||||
- 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
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Lint
|
||||
|
||||
clippy-riscv:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Run clippy on all packages targeting RISC-V.
|
||||
- name: clippy (esp-riscv-rt)
|
||||
run: cargo +stable clippy --manifest-path=esp-riscv-rt/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32c2-hal)
|
||||
run: cargo +stable clippy --manifest-path=esp32c2-hal/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32c3-hal)
|
||||
run: cargo +stable clippy --manifest-path=esp32c3-hal/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32c6-hal)
|
||||
run: cargo +stable clippy --manifest-path=esp32c6-hal/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32h2-hal)
|
||||
run: cargo +stable clippy --manifest-path=esp32h2-hal/Cargo.toml -- --no-deps
|
||||
|
||||
clippy-xtensa:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
ldproxy: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Run clippy on all packages targeting Xtensa.
|
||||
#
|
||||
# The ESP32-S2 requires some additional information in order for the
|
||||
# atomic emulation crate to build.
|
||||
- name: clippy (esp32-hal)
|
||||
run: cargo +esp clippy --manifest-path=esp32-hal/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32s2-hal)
|
||||
run: cargo +esp clippy --manifest-path=esp32s2-hal/Cargo.toml -- --no-deps
|
||||
- name: clippy (esp32s3-hal)
|
||||
run: cargo +esp clippy --manifest-path=esp32s3-hal/Cargo.toml -- --no-deps
|
||||
# Format
|
||||
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Some of the items in 'rustfmt.toml' require the nightly release
|
||||
# channel, so we must use this channel for the formatting checks
|
||||
# to succeed.
|
||||
- uses: actions/checkout@v4
|
||||
# Some of the configuration items in 'rustfmt.toml' require the 'nightly'
|
||||
# release channel:
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Check the formatting of all packages.
|
||||
- name: rustfmt (esp-hal-common)
|
||||
run: cargo fmt --all --manifest-path=esp-hal-common/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-riscv-rt)
|
||||
run: cargo fmt --all --manifest-path=esp-riscv-rt/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32c2-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32c2-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32c3-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32c3-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32c6-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32c6-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32h2-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32h2-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32s2-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32s2-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp32s3-hal)
|
||||
run: cargo fmt --all --manifest-path=esp32s3-hal/Cargo.toml -- --check
|
||||
# Check the formatting of all packages:
|
||||
- run: cargo xtask fmt-packages --check
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Changelog
|
||||
# host tests
|
||||
|
||||
changelog:
|
||||
name: Changelog
|
||||
host-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check that changelog updated
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
changeLogPath: CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the CHANGELOG.md file."
|
||||
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
|
||||
99
.github/workflows/documentation.yml
vendored
Normal file
99
.github/workflows/documentation.yml
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
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
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
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: 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/"
|
||||
|
||||
deploy:
|
||||
# Add a dependency to the assemble job:
|
||||
needs: assemble
|
||||
|
||||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment:
|
||||
permissions:
|
||||
pages: write # to deploy to Pages
|
||||
id-token: write # to verify the deployment originates from an appropriate source
|
||||
|
||||
# Deploy to the github-pages environment:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
# Specify runner + deployment step:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
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
|
||||
16
.github/workflows/issue_handler.yml
vendored
Normal file
16
.github/workflows/issue_handler.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Add new issues to project
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/esp-rs/projects/2
|
||||
github-token: ${{ secrets.PAT }}
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@ -13,12 +13,12 @@ Cargo.lock
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Other
|
||||
**/settings.json
|
||||
|
||||
# wokwi related files
|
||||
# Wokwi related files
|
||||
diagram.json
|
||||
wokwi.toml
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
# We'll ignore VS Code settings (at least for now...)
|
||||
**/.vscode/settings.json
|
||||
|
||||
# Ignore generated documentation
|
||||
docs/
|
||||
|
||||
136
CHANGELOG.md
136
CHANGELOG.md
@ -1,136 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
Please note that only changes to the `esp-hal-common` package are tracked in this CHANGELOG.
|
||||
|
||||
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
|
||||
|
||||
- Add bare-bones PSRAM support for ESP32 (#506)
|
||||
- Add initial support for the ESP32-H2 (#513)
|
||||
- Add bare-bones PSRAM support for ESP32-S3 (#517)
|
||||
- Add async support to the I2C driver (#519)
|
||||
- Add initial support for RSA in ESP32-H2 (#526)
|
||||
- Add initial support for SHA in ESP32-H2 (#527)
|
||||
- Add initial support for AES in ESP32-H2 (#528)
|
||||
- Add blinky_erased_pins example for ESP32-H2 (#530)
|
||||
- Add initial support for I2C in ESP32-H2 (#538)
|
||||
- Implement Copy and Eq for EspTwaiError (#540)
|
||||
- Add LEDC hardware fade support
|
||||
- Added support for multicore async GPIO (#542)
|
||||
- Add initial support for MCPWM in ESP32-H2 (#544)
|
||||
- Add some miscellaneous examples for the ESP32-H2 (#548)
|
||||
- Add initial support for PCNT in ESP32-H2 (#551)
|
||||
- Add initial support for RMT in ESP32-H2 (#556)
|
||||
- Add a fn to poll DMA transfers
|
||||
- Add initial support for LEDC in ESP32-H2 (#560)
|
||||
- Add initial support for ASSIST_DEBUG in ESP32-H2 (#566)
|
||||
- Add all `SPI` examples for the ESP32-H2 (#549)
|
||||
- Add initial support for ADC in ESP32-H2 (#564)
|
||||
- Simplify the `Delay` driver, derive `Clone` and `Copy` (#568)
|
||||
- Add `embassy_serial` and `embassy_wait` examples for ESP32-H2 (#569)
|
||||
- Fix Async GPIO not disabling interupts on chips with multiple banks (#572)
|
||||
- Add unified field-based efuse access
|
||||
- Add `timer_interrupt` example in ESP32-H2 and refactor `clk_src` configuration (#576)
|
||||
- Move `esp-riscv-rt` into esp-hal (#578)
|
||||
- Add initial implementation of radio clocks for ESP32-H2 (#577)
|
||||
- Add initial support for `esp-hal-smartled` in ESP32-H2 (#589)
|
||||
- Add CRC functions from ESP ROM
|
||||
- Add initial support for RNG in ESP32-H2 (#591)
|
||||
- Add a `debug` feature to enable the PACs' `impl-register-debug` feature (#596)
|
||||
- Add initial support for `I2S` in ESP32-H2 (#597)
|
||||
|
||||
### Changed
|
||||
|
||||
- Move core interrupt handling from Flash to RAM for RISC-V chips (ESP32-H2, ESP32-C2, ESP32-C3, ESP32-C6) (#541)
|
||||
- Change LED pin to GPIO2 in ESP32 blinky example (#581)
|
||||
- Udpate ESP32-H2 and C6 ESP32-clocks and remove i2c_clock for all chips but ESP32 (#592)
|
||||
|
||||
### Fixed
|
||||
|
||||
- DMA is supported for SPI3 on ESP32-S3 (#507)
|
||||
- `change_bus_frequency` is now available on `SpiDma` (#529)
|
||||
- Fixed a bug where a GPIO interrupt could erroneously fire again causing the next `await` on that pin to instantly return `Poll::Ok` (#537)
|
||||
- Set `vecbase` on core 1 (ESP32, ESP32-S3) (#536)
|
||||
- ESP32-S3: Move PSRAM related function to RAM (#546)
|
||||
- ADC driver will now apply attenuation values to the correct ADC's channels. (#554)
|
||||
- Sometimes half-duplex non-DMA SPI reads were reading garbage in non-release mode (#552)
|
||||
- ESP32-C3: Fix GPIO5 ADC channel id (#562)
|
||||
- ESP32-H2: Fix direct-boot feature
|
||||
- ESP32-C6: Support FOSC CLK calibration for ECO1+ chip revisions
|
||||
- Fixed CI by pinning the log crate to 0.4.18 (#600)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve examples documentation (#533)
|
||||
- esp32h2-hal: added README (#585)
|
||||
|
||||
### Breaking
|
||||
|
||||
- Significantly simplified user-facing GPIO pin types. (#553)
|
||||
|
||||
## [0.9.0] - 2023-05-02
|
||||
|
||||
### Added
|
||||
|
||||
- Add bare-bones PSRAM support for ESP32-S2 (#493)
|
||||
- Add `DEBUG_ASSIST` functionality (#484)
|
||||
- Add RSA peripheral support (#467)
|
||||
- Add PeripheralClockControl argument to `timg`, `wdt`, `sha`, `usb-serial-jtag` and `uart` constructors (#463)
|
||||
- Added API to raise and reset software interrupts (#426)
|
||||
- Implement `embedded_hal_nb::serial::*` traits for `UsbSerialJtag` (#498)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `get_wakeup_cause` comparison error (#472)
|
||||
- Use 192 as mclk_multiple for 24-bit I2S (#471)
|
||||
- Fix `CpuControl::start_app_core` signature (#466)
|
||||
- Move `rwtext` after other RAM data sections (#464)
|
||||
- ESP32-C3: Disable `usb_pad_enable` when setting GPIO18/19 to input/output (#461)
|
||||
- Fix 802.15.4 clock enabling (ESP32-C6) (#458)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update `embedded-hal-async` and `embassy-*` dependencies (#488)
|
||||
- Update to `embedded-hal@1.0.0-alpha.10` and `embedded-hal-nb@1.0.0-alpha.2` (#487)
|
||||
- Let users configure the LEDC output pin as open-drain (#474)
|
||||
- Use bitflags to decode wakeup cause (#473)
|
||||
- Minor linker script additions (#470)
|
||||
- Minor documentation improvements (#460)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove unnecessary generic from `UsbSerialJtag` driver (#492)
|
||||
- Remove `#[doc(inline)]` from esp-hal-common re-exports (#490)
|
||||
|
||||
## [0.8.0] - 2023-03-27
|
||||
|
||||
## [0.7.1] - 2023-02-22
|
||||
|
||||
## [0.7.0] - 2023-02-21
|
||||
|
||||
## [0.5.0] - 2023-01-26
|
||||
|
||||
## [0.4.0] - 2022-12-12
|
||||
|
||||
## [0.3.0] - 2022-11-17
|
||||
|
||||
## [0.2.0] - 2022-09-13
|
||||
|
||||
## [0.1.0] - 2022-08-05
|
||||
|
||||
[unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.9.0...HEAD
|
||||
[0.9.0]: https://github.com/esp-rs/esp-hal/compare/v0.8.0...v0.9.0
|
||||
[0.8.0]: https://github.com/esp-rs/esp-hal/compare/v0.7.1...v0.8.0
|
||||
[0.7.1]: https://github.com/esp-rs/esp-hal/compare/v0.7.0...v0.7.1
|
||||
[0.7.0]: https://github.com/esp-rs/esp-hal/compare/v0.5.0...v0.7.0
|
||||
[0.5.0]: https://github.com/esp-rs/esp-hal/compare/v0.4.0...v0.5.0
|
||||
[0.4.0]: https://github.com/esp-rs/esp-hal/compare/v0.3.0...v0.4.0
|
||||
[0.3.0]: https://github.com/esp-rs/esp-hal/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/esp-rs/esp-hal/compare/v0.1.0...v0.2.0
|
||||
[0.1.0]: https://github.com/esp-rs/esp-hal/releases/tag/v0.1.0
|
||||
28
Cargo.toml
Normal file
28
Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["xtask"]
|
||||
exclude = [
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-build",
|
||||
"esp-config",
|
||||
"esp-hal",
|
||||
"esp-hal-embassy",
|
||||
"esp-hal-procmacros",
|
||||
"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,105 +1,60 @@
|
||||
# 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**, and **ESP32-S2/S3** from Espressif.
|
||||
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_
|
||||
|
||||
This project is still in the early stages of development, and as such there should be no expectation of API stability. A significant number of peripherals currently have drivers implemented (you can see a full list [here]) but have varying levels of functionality. For most basic tasks, this should be usable already.
|
||||
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.
|
||||
|
||||
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 crate's README.
|
||||
These packages are all `no_std`; if you are looking for `std` support, please use [esp-idf-svc] instead.
|
||||
|
||||
| Crate | Target | Technical Reference Manual |
|
||||
| :-----------: | :----------------------------: | :------------------------: |
|
||||
| [esp32-hal] | `xtensa-esp32-none-elf` | [ESP32] |
|
||||
| [esp32c2-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C2] |
|
||||
| [esp32c3-hal] | `riscv32imc-unknown-none-elf` | [ESP32-C3] |
|
||||
| [esp32c6-hal] | `riscv32imac-unknown-none-elf` | [ESP32-C6] |
|
||||
| [esp32h2-hal] | `riscv32imac-unknown-none-elf` | _Currently unavailable_ |
|
||||
| [esp32s2-hal] | `xtensa-esp32s2-none-elf` | [ESP32-S2] |
|
||||
| [esp32s3-hal] | `xtensa-esp32s3-none-elf` | [ESP32-S3] |
|
||||
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.
|
||||
|
||||
[here]: https://github.com/esp-rs/esp-hal/issues/19
|
||||
[esp-idf-hal]: https://github.com/esp-rs/esp-idf-hal
|
||||
> [!NOTE]
|
||||
>
|
||||
> 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-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
|
||||
[esp32-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32-hal
|
||||
[esp32c2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c2-hal
|
||||
[esp32c3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal
|
||||
[esp32c6-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c6-hal
|
||||
[esp32h2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32h2-hal
|
||||
[esp32s2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s2-hal
|
||||
[esp32s3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s3-hal
|
||||
[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-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
|
||||
|
||||
## Quickstart
|
||||
## Getting Started
|
||||
|
||||
We recommend using [cargo-generate] and [esp-template] in order to generate a new project with all the required dependencies and configuration:
|
||||
For information relating to the development of Rust applications on ESP devices, please first read [The Rust on ESP Book].
|
||||
|
||||
```bash
|
||||
$ cargo install cargo-generate
|
||||
$ cargo generate -a esp-rs/esp-template
|
||||
```
|
||||
For information about the HAL and how to use it in your own projects, please refer to the [documentation].
|
||||
|
||||
For more information on using this template, please refer to [its README].
|
||||
[The Rust on ESP Book]: https://esp-rs.github.io/book/
|
||||
[documentation]: https://docs.esp-rs.org/esp-hal/
|
||||
|
||||
[cargo-generate]: https://github.com/cargo-generate/cargo-generate
|
||||
[esp-template]: https://github.com/esp-rs/esp-template
|
||||
[its readme]: https://github.com/esp-rs/esp-template/blob/main/README.md
|
||||
## Resources
|
||||
|
||||
## Ancillary Crates
|
||||
- [The Rust Programming Language](https://doc.rust-lang.org/book/)
|
||||
- [The Embedded Rust Book](https://docs.rust-embedded.org/book/index.html)
|
||||
- [The Embedonomicon](https://docs.rust-embedded.org/embedonomicon/)
|
||||
- [The Rust on ESP Book](https://esp-rs.github.io/book/)
|
||||
- [Embedded Rust (no_std) on Espressif](https://esp-rs.github.io/no_std-training/)
|
||||
|
||||
There are a number of other crates within the [esp-rs organization] which can be used in conjunction with `esp-hal`:
|
||||
## Crates
|
||||
|
||||
| Crate | Description |
|
||||
| :-------------: | :----------------------------------------------------------------------------: |
|
||||
| [esp-alloc] | A simple `no_std` heap allocator |
|
||||
| [esp-backtrace] | Backtrace support for bare-metal applications |
|
||||
| [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/Bluetooth LE support |
|
||||
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.
|
||||
|
||||
[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-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
|
||||
## Contributing
|
||||
|
||||
## MSRV
|
||||
We have a number of living documents to aid contributing to the project, please give these a read before modifying code:
|
||||
|
||||
The **M**inimum **S**upported **R**ust **V**ersions are:
|
||||
|
||||
- `1.65.0` for RISC-V devices (**ESP32-C2**, **ESP32-C3**, **ESP32-C6**, **ESP32-H2**)
|
||||
- `1.65.0` for Xtensa devices (**ESP32**, **ESP32-S2**, **ESP32-S3**)
|
||||
- `1.67.0` for all `async` examples (`embassy_hello_world`, `embassy_wait`, etc.)
|
||||
|
||||
Note that targeting the Xtensa ISA currently requires the use of the [esp-rs/rust] compiler fork. The [esp-rs/rust-build] repository has pre-compiled release artifacts for most common platforms, and provides installation scripts to aid you in the process.
|
||||
|
||||
RISC-V is officially supported by the official Rust compiler.
|
||||
|
||||
[esp-rs/rust]: https://github.com/esp-rs/rust
|
||||
[esp-rs/rust-build]: https://github.com/esp-rs/rust-build
|
||||
|
||||
## Git Hooks
|
||||
|
||||
We provide a simple `pre-commit` hook to verify the formatting of each package prior to committing changes. This can be enabled by placing it in 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
|
||||
|
||||
@ -110,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-common/.gitignore
vendored
1
esp-hal-common/.gitignore
vendored
@ -1 +0,0 @@
|
||||
rust-toolchain.toml
|
||||
@ -1,121 +0,0 @@
|
||||
[package]
|
||||
name = "esp-hal-common"
|
||||
version = "0.9.0"
|
||||
authors = [
|
||||
"Jesse Braham <jesse@beta7.io>",
|
||||
"Björn Quentin <bjoern.quentin@mobile-j.de>",
|
||||
]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
description = "HAL implementations for peripherals common among Espressif devices; should not be used directly"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.2.1"
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.1.1"
|
||||
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-alpha.10", optional = true, package = "embedded-hal" }
|
||||
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true }
|
||||
esp-synopsys-usb-otg = { version = "0.3.1", optional = true, features = ["fs", "esp32sx"] }
|
||||
fugit = "0.3.6"
|
||||
log = "=0.4.18"
|
||||
lock_api = { version = "0.4.9", optional = true }
|
||||
nb = "1.1.0"
|
||||
paste = "1.0.12"
|
||||
procmacros = { version = "0.5.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
strum = { version = "0.24.1", default-features = false, features = ["derive"] }
|
||||
void = { version = "1.0.2", default-features = false }
|
||||
usb-device = { version = "0.2.9", optional = true }
|
||||
|
||||
# async
|
||||
embedded-hal-async = { version = "0.2.0-alpha.1", optional = true }
|
||||
embassy-sync = { version = "0.2.0", optional = true }
|
||||
embassy-time = { version = "0.1.1", features = ["nightly"], optional = true }
|
||||
embassy-futures = { version = "0.1.0", optional = true }
|
||||
|
||||
# RISC-V
|
||||
esp-riscv-rt = { version = "0.3.0", path = "../esp-riscv-rt", optional = true }
|
||||
riscv-atomic-emulation-trap = { version = "0.4.0", optional = true }
|
||||
|
||||
# Xtensa
|
||||
xtensa-lx = { version = "0.8.0", optional = true }
|
||||
xtensa-lx-rt = { version = "0.15.0", optional = true }
|
||||
|
||||
# Part of `ufmt` containing only `uWrite` trait
|
||||
ufmt-write = { version = "0.1.0", optional = true }
|
||||
|
||||
# IMPORTANT:
|
||||
# Each supported device MUST have its PAC included below along with a
|
||||
# corresponding feature.
|
||||
esp32 = { version = "0.24.0", features = ["critical-section"], optional = true }
|
||||
esp32c2 = { version = "0.12.0", features = ["critical-section"], optional = true }
|
||||
esp32c3 = { version = "0.15.0", features = ["critical-section"], optional = true }
|
||||
esp32c6 = { version = "0.5.0", features = ["critical-section"], optional = true }
|
||||
esp32h2 = { version = "0.1.0", features = ["critical-section"], optional = true }
|
||||
esp32s2 = { version = "0.15.0", features = ["critical-section"], optional = true }
|
||||
esp32s3 = { version = "0.19.0", features = ["critical-section"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
basic-toml = "0.1.2"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api", "procmacros/esp32"]
|
||||
esp32c2 = ["esp32c2/rt", "riscv", "procmacros/esp32c2"]
|
||||
esp32c3 = ["esp32c3/rt", "riscv", "procmacros/esp32c3"]
|
||||
esp32c6 = ["esp32c6/rt", "riscv", "procmacros/esp32c6"]
|
||||
esp32h2 = ["esp32h2/rt", "riscv", "procmacros/esp32h2"]
|
||||
esp32s2 = ["esp32s2/rt", "xtensa", "xtensa-lx/esp32s2", "xtensa-lx-rt/esp32s2", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s2"]
|
||||
esp32s3 = ["esp32s3/rt", "xtensa", "xtensa-lx/esp32s3", "xtensa-lx-rt/esp32s3", "lock_api", "esp-synopsys-usb-otg", "usb-device", "procmacros/esp32s3"]
|
||||
|
||||
esp32_40mhz = []
|
||||
esp32_26mhz = []
|
||||
|
||||
esp32c2_40mhz = []
|
||||
esp32c2_26mhz = []
|
||||
|
||||
psram_2m = []
|
||||
psram_4m = []
|
||||
psram_8m = []
|
||||
|
||||
# Implement the `embedded-hal==1.0.0-alpha.x` traits
|
||||
eh1 = ["embedded-hal-1", "embedded-hal-nb", "embedded-can"]
|
||||
|
||||
# To support `ufmt`
|
||||
ufmt = ["ufmt-write"]
|
||||
|
||||
# To use vectored interrupts (calling the handlers defined in the PAC)
|
||||
vectored = ["procmacros/interrupt"]
|
||||
|
||||
# Implement the `embedded-hal-async==1.0.0-alpha.x` traits
|
||||
async = ["embedded-hal-async", "eh1", "embassy-sync", "embassy-futures"]
|
||||
embassy = ["embassy-time"]
|
||||
|
||||
embassy-time-systick = []
|
||||
embassy-time-timg0 = []
|
||||
|
||||
interrupt-preemption = []
|
||||
|
||||
# Architecture-specific features (intended for internal use)
|
||||
riscv = ["critical-section/restore-state-u8", "procmacros/riscv", "esp-riscv-rt", "riscv-atomic-emulation-trap", "esp-riscv-rt/zero-bss"]
|
||||
xtensa = ["critical-section/restore-state-u32", "procmacros/xtensa"]
|
||||
|
||||
# Initialize / clear data sections and RTC memory
|
||||
rv-init-data = ["esp-riscv-rt/init-data", "esp-riscv-rt/init-rw-text"]
|
||||
rv-zero-rtc-bss = ["esp-riscv-rt/zero-rtc-fast-bss"]
|
||||
rv-init-rtc-data = ["esp-riscv-rt/init-rtc-fast-data", "esp-riscv-rt/init-rtc-fast-text"]
|
||||
|
||||
# Enable the `impl-register-debug` feature for the selected PAC
|
||||
debug = [
|
||||
"esp32?/impl-register-debug",
|
||||
"esp32c2?/impl-register-debug",
|
||||
"esp32c3?/impl-register-debug",
|
||||
"esp32c6?/impl-register-debug",
|
||||
"esp32h2?/impl-register-debug",
|
||||
"esp32s2?/impl-register-debug",
|
||||
"esp32s3?/impl-register-debug",
|
||||
]
|
||||
@ -1,37 +0,0 @@
|
||||
# esp-hal-common
|
||||
|
||||
[](https://crates.io/crates/esp-hal-common)
|
||||
[](https://docs.rs/esp-hal-common)
|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
`no_std` HAL implementations for the peripherals which are common among Espressif devices. Implements a number of the traits defined by [embedded-hal](https://github.com/rust-embedded/embedded-hal).
|
||||
|
||||
This crate should not be used directly; you should use one of the device-specific HAL crates instead:
|
||||
|
||||
- [esp32-hal](../esp32-hal/README.md)
|
||||
- [esp32c2-hal](../esp32c2-hal/README.md)
|
||||
- [esp32c3-hal](../esp32c3-hal/README.md)
|
||||
- [esp32c6-hal](../esp32c6-hal/README.md)
|
||||
- [esp32h2-hal](../esp32h2-hal/README.md)
|
||||
- [esp32s2-hal](../esp32s2-hal/README.md)
|
||||
- [esp32s3-hal](../esp32s3-hal/README.md)
|
||||
|
||||
## [Documentation]
|
||||
|
||||
[documentation]: https://docs.rs/esp-hal-common/
|
||||
|
||||
## 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,252 +0,0 @@
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
// Macros taken from:
|
||||
// https://github.com/TheDan64/inkwell/blob/36c3b10/src/lib.rs#L81-L110
|
||||
|
||||
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),*);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_used_features {
|
||||
( $($all:tt),* ) => {
|
||||
#[cfg(not(any($(feature = $all),*)))]
|
||||
compile_error!(concat!("One of the feature flags must be provided: ", $($all, ", "),*));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_unique_used_features {
|
||||
( $($all:tt),* ) => {
|
||||
assert_unique_features!($($all),*);
|
||||
assert_used_features!($($all),*);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum Arch {
|
||||
#[serde(rename = "riscv")]
|
||||
RiscV,
|
||||
#[serde(rename = "xtensa")]
|
||||
Xtensa,
|
||||
}
|
||||
|
||||
impl core::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 core::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>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Config {
|
||||
pub device: Device,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 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"
|
||||
);
|
||||
|
||||
// Handle the features for the ESP32's different crystal frequencies:
|
||||
#[cfg(feature = "esp32")]
|
||||
{
|
||||
assert_unique_used_features!("esp32_26mhz", "esp32_40mhz");
|
||||
}
|
||||
|
||||
// Handle the features for the ESP32-C2's different crystal frequencies:
|
||||
#[cfg(feature = "esp32c2")]
|
||||
{
|
||||
assert_unique_used_features!("esp32c2_26mhz", "esp32c2_40mhz");
|
||||
}
|
||||
|
||||
// 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_toml_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("devices")
|
||||
.join(format!("{}.toml", device_name))
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
|
||||
let config = fs::read_to_string(chip_toml_path).unwrap();
|
||||
let config: Config = basic_toml::from_str(&config).unwrap();
|
||||
let device = config.device;
|
||||
|
||||
// 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}");
|
||||
}
|
||||
|
||||
// check PSRAM features are only given if the target supports PSRAM
|
||||
if !&device.peripherals.contains(&String::from("psram"))
|
||||
&& (cfg!(feature = "psram_2m") || cfg!(feature = "psram_4m") || cfg!(feature = "psram_8m"))
|
||||
{
|
||||
panic!("The target does not support PSRAM");
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
if cfg!(feature = "esp32") || cfg!(feature = "esp32s2") || cfg!(feature = "esp32s3") {
|
||||
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x")).unwrap();
|
||||
fs::copy("ld/xtensa/rom.x", out.join("alias.x")).unwrap();
|
||||
} else {
|
||||
fs::copy("ld/riscv/hal-defaults.x", out.join("hal-defaults.x")).unwrap();
|
||||
fs::copy("ld/riscv/asserts.x", out.join("asserts.x")).unwrap();
|
||||
fs::copy("ld/riscv/debug.x", out.join("debug.x")).unwrap();
|
||||
}
|
||||
copy_dir_all("ld/sections", &out).unwrap();
|
||||
|
||||
gen_efuse_table(device_name, out);
|
||||
}
|
||||
|
||||
fn copy_dir_all(
|
||||
src: impl AsRef<std::path::Path>,
|
||||
dst: impl AsRef<std::path::Path>,
|
||||
) -> std::io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_efuse_table(device_name: &str, out_dir: impl AsRef<std::path::Path>) {
|
||||
use std::io::{BufRead, Write};
|
||||
|
||||
let src_path = std::path::PathBuf::from(format!("src/soc/{device_name}/efuse.csv"));
|
||||
let out_path = out_dir.as_ref().join("efuse_fields.rs");
|
||||
|
||||
println!("cargo:rerun-if-changed={}", src_path.display());
|
||||
|
||||
let mut writer = std::fs::File::create(out_path).unwrap();
|
||||
let mut reader = std::io::BufReader::new(std::fs::File::open(src_path).unwrap());
|
||||
let mut line = String::with_capacity(128);
|
||||
|
||||
while reader.read_line(&mut line).unwrap() > 0 {
|
||||
if line.ends_with("\n") {
|
||||
line.pop();
|
||||
if line.ends_with("\r") {
|
||||
line.pop();
|
||||
}
|
||||
}
|
||||
// drop comment and trim
|
||||
line.truncate(
|
||||
if let Some((pfx, _cmt)) = line.split_once("#") {
|
||||
pfx
|
||||
} else {
|
||||
&line
|
||||
}
|
||||
.trim()
|
||||
.len(),
|
||||
);
|
||||
// skip empty
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut fields = line.split(",");
|
||||
match (
|
||||
fields.next().map(|s| s.trim().replace(".", "_")),
|
||||
fields
|
||||
.next()
|
||||
.map(|s| s.trim().replace(|c: char| !c.is_ascii_digit(), "")),
|
||||
fields
|
||||
.next()
|
||||
.map(|s| s.trim())
|
||||
.and_then(|s| s.parse::<u32>().ok()),
|
||||
fields
|
||||
.next()
|
||||
.map(|s| s.trim())
|
||||
.and_then(|s| s.parse::<u32>().ok()),
|
||||
fields.next().map(|s| s.trim()),
|
||||
) {
|
||||
(Some(name), Some(block), Some(bit_off), Some(bit_len), Some(desc)) => {
|
||||
writeln!(writer, "/// {desc}").unwrap();
|
||||
writeln!(
|
||||
writer,
|
||||
"pub const {name}: EfuseField = EfuseField::new(EfuseBlock::Block{block}, {bit_off}, {bit_len});"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
other => eprintln!("Invalid data: {other:?}"),
|
||||
}
|
||||
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
|
||||
|
||||
SECTIONS {
|
||||
.external.data :
|
||||
{
|
||||
_external_data_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.external.data .external.data.*)
|
||||
_external_data_end = ABSOLUTE(.);
|
||||
} > psram_seg AT > RODATA
|
||||
|
||||
.external.bss (NOLOAD) :
|
||||
{
|
||||
_external_bss_start = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
*(.external.bss .external.bss.*)
|
||||
_external_bss_end = ABSOLUTE(.);
|
||||
} > psram_seg
|
||||
|
||||
.external.noinit (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.external.noinit .external.noinit.*)
|
||||
} > psram_seg
|
||||
|
||||
/* must be last segment using psram_seg */
|
||||
.external_heap_start (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_external_heap_start = ABSOLUTE(.);
|
||||
} > psram_seg
|
||||
}
|
||||
|
||||
_external_ram_start = ABSOLUTE(ORIGIN(psram_seg));
|
||||
_external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg));
|
||||
@ -1,10 +0,0 @@
|
||||
|
||||
|
||||
SECTIONS {
|
||||
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
*(.literal .text .literal.* .text.*)
|
||||
} > ROTEXT
|
||||
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
REGION_ALIAS("ROTEXT", irom_seg);
|
||||
REGION_ALIAS("RWTEXT", iram_seg);
|
||||
REGION_ALIAS("RODATA", drom_seg);
|
||||
REGION_ALIAS("RWDATA", dram_seg);
|
||||
REGION_ALIAS("RTC_FAST_RWTEXT", rtc_fast_iram_seg);
|
||||
REGION_ALIAS("RTC_FAST_RWDATA", rtc_fast_dram_seg);
|
||||
@ -1,58 +0,0 @@
|
||||
use crate::{
|
||||
aes::{Aes, Aes128, Aes256, AesFlavour, ALIGN_SIZE},
|
||||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||||
};
|
||||
|
||||
impl<'d> Aes<'d> {
|
||||
pub(super) fn init(&mut self, peripheral_clock_control: &mut PeripheralClockControl) {
|
||||
peripheral_clock_control.enable(PeripheralEnable::Aes);
|
||||
self.write_dma(false);
|
||||
}
|
||||
|
||||
fn write_dma(&mut self, enable_dma: bool) {
|
||||
match enable_dma {
|
||||
true => self.aes.dma_enable.write(|w| w.dma_enable().set_bit()),
|
||||
false => self.aes.dma_enable.write(|w| w.dma_enable().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_key(&mut self, key: &[u8]) {
|
||||
debug_assert!(key.len() <= 8 * ALIGN_SIZE);
|
||||
debug_assert_eq!(key.len() % ALIGN_SIZE, 0);
|
||||
Self::write_to_regset(key, 8, &mut self.aes.key_0);
|
||||
}
|
||||
|
||||
pub(super) fn write_block(&mut self, block: &[u8]) {
|
||||
debug_assert_eq!(block.len(), 4 * ALIGN_SIZE);
|
||||
Self::write_to_regset(block, 4, &mut self.aes.text_in_0);
|
||||
}
|
||||
|
||||
pub(super) fn write_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.aes.mode, mode);
|
||||
}
|
||||
|
||||
pub(super) fn write_start(&mut self) {
|
||||
self.aes.trigger.write(|w| w.trigger().set_bit())
|
||||
}
|
||||
|
||||
pub(super) fn read_idle(&mut self) -> bool {
|
||||
self.aes.state.read().state().bits() == 0
|
||||
}
|
||||
|
||||
pub(super) fn read_block(&self, block: &mut [u8]) {
|
||||
debug_assert_eq!(block.len(), 4 * ALIGN_SIZE);
|
||||
Self::read_from_regset(block, 4, &self.aes.text_out_0);
|
||||
}
|
||||
}
|
||||
|
||||
impl AesFlavour for Aes128 {
|
||||
type KeyType<'b> = &'b [u8; 16];
|
||||
const ENCRYPT_MODE: u32 = 0;
|
||||
const DECRYPT_MODE: u32 = 4;
|
||||
}
|
||||
|
||||
impl AesFlavour for Aes256 {
|
||||
type KeyType<'b> = &'b [u8; 32];
|
||||
const ENCRYPT_MODE: u32 = 2;
|
||||
const DECRYPT_MODE: u32 = 6;
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
use crate::{
|
||||
aes::{Aes, Aes128, Aes256, AesFlavour, ALIGN_SIZE},
|
||||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||||
};
|
||||
|
||||
impl<'d> Aes<'d> {
|
||||
pub(super) fn init(&mut self, peripheral_clock_control: &mut PeripheralClockControl) {
|
||||
peripheral_clock_control.enable(PeripheralEnable::Aes);
|
||||
self.write_dma(false);
|
||||
}
|
||||
|
||||
fn write_dma(&mut self, enable_dma: bool) {
|
||||
match enable_dma {
|
||||
true => self.aes.dma_enable.write(|w| w.dma_enable().set_bit()),
|
||||
false => self.aes.dma_enable.write(|w| w.dma_enable().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_key(&mut self, key: &[u8]) {
|
||||
debug_assert!(key.len() <= self.aes.key_.len() * ALIGN_SIZE);
|
||||
debug_assert_eq!(key.len() % ALIGN_SIZE, 0);
|
||||
Self::write_to_regset(key, self.aes.key_.len(), &mut self.aes.key_[0]);
|
||||
}
|
||||
|
||||
pub(super) fn write_block(&mut self, block: &[u8]) {
|
||||
debug_assert_eq!(block.len(), self.aes.text_in_.len() * ALIGN_SIZE);
|
||||
Self::write_to_regset(block, self.aes.text_in_.len(), &mut self.aes.text_in_[0]);
|
||||
}
|
||||
|
||||
pub(super) fn write_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.aes.mode, mode);
|
||||
}
|
||||
|
||||
pub(super) fn write_start(&mut self) {
|
||||
self.aes.trigger.write(|w| w.trigger().set_bit())
|
||||
}
|
||||
|
||||
pub(super) fn read_idle(&mut self) -> bool {
|
||||
self.aes.state.read().state().bits() == 0
|
||||
}
|
||||
|
||||
pub(super) fn read_block(&self, block: &mut [u8]) {
|
||||
debug_assert_eq!(block.len(), self.aes.text_out_.len() * ALIGN_SIZE);
|
||||
Self::read_from_regset(block, self.aes.text_out_.len(), &self.aes.text_out_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
impl AesFlavour for Aes128 {
|
||||
type KeyType<'b> = &'b [u8; 16];
|
||||
const ENCRYPT_MODE: u32 = 0;
|
||||
const DECRYPT_MODE: u32 = 4;
|
||||
}
|
||||
|
||||
impl AesFlavour for Aes256 {
|
||||
type KeyType<'b> = &'b [u8; 32];
|
||||
const ENCRYPT_MODE: u32 = 2;
|
||||
const DECRYPT_MODE: u32 = 6;
|
||||
}
|
||||
@ -1,187 +0,0 @@
|
||||
//! Advanced Encryption Standard (AES) support.
|
||||
//!
|
||||
//! This module provides functions and structs for AES encryption and
|
||||
//! decryption.
|
||||
//!
|
||||
//! ### Implementation State
|
||||
//! * DMA mode is currently not supported.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals::{
|
||||
generic::{Readable, Reg, RegisterSpec, Resettable, Writable},
|
||||
AES,
|
||||
},
|
||||
system::PeripheralClockControl,
|
||||
};
|
||||
|
||||
#[cfg_attr(esp32, path = "esp32.rs")]
|
||||
#[cfg_attr(esp32s3, path = "esp32s3.rs")]
|
||||
#[cfg_attr(esp32s2, path = "esp32s2.rs")]
|
||||
#[cfg_attr(esp32c3, path = "esp32cX.rs")]
|
||||
#[cfg_attr(esp32c6, path = "esp32cX.rs")]
|
||||
#[cfg_attr(esp32h2, path = "esp32cX.rs")]
|
||||
mod aes_spec_impl;
|
||||
|
||||
const ALIGN_SIZE: usize = core::mem::size_of::<u32>();
|
||||
|
||||
/// AES peripheral container
|
||||
pub struct Aes<'d> {
|
||||
aes: PeripheralRef<'d, AES>,
|
||||
}
|
||||
|
||||
impl<'d> Aes<'d> {
|
||||
pub fn new(
|
||||
aes: impl Peripheral<P = AES> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(aes);
|
||||
let mut ret = Self { aes: aes };
|
||||
ret.init(peripheral_clock_control);
|
||||
ret
|
||||
}
|
||||
|
||||
fn write_to_regset<T>(input: &[u8], n_offset: usize, reg_0: &mut Reg<T>)
|
||||
where
|
||||
T: RegisterSpec<Ux = u32> + Resettable + Writable,
|
||||
{
|
||||
let chunks = input.chunks_exact(ALIGN_SIZE);
|
||||
for (offset, chunk) in (0..n_offset).zip(chunks) {
|
||||
let to_write = u32::from_ne_bytes(chunk.try_into().unwrap());
|
||||
unsafe {
|
||||
let p = reg_0.as_ptr().add(offset);
|
||||
p.write_volatile(to_write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_from_regset<T>(out_buf: &mut [u8], n_offset: usize, reg_0: &Reg<T>)
|
||||
where
|
||||
T: RegisterSpec<Ux = u32> + Readable,
|
||||
{
|
||||
let chunks = out_buf.chunks_exact_mut(ALIGN_SIZE);
|
||||
for (offset, chunk) in (0..n_offset).zip(chunks) {
|
||||
unsafe {
|
||||
let p = reg_0.as_ptr().add(offset);
|
||||
let read_val: [u8; ALIGN_SIZE] = p.read_volatile().to_ne_bytes();
|
||||
chunk.copy_from_slice(&read_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_register<T>(reg: &mut Reg<T>, data: u32)
|
||||
where
|
||||
T: RegisterSpec<Ux = u32> + Resettable + Writable,
|
||||
{
|
||||
reg.write(|w| unsafe { w.bits(data) });
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
/// Specifications for AES flavours
|
||||
pub trait AesFlavour {
|
||||
type KeyType<'b>;
|
||||
const ENCRYPT_MODE: u32;
|
||||
const DECRYPT_MODE: u32;
|
||||
}
|
||||
}
|
||||
|
||||
use sealed::AesFlavour;
|
||||
|
||||
/// Marker type for AES-128
|
||||
pub struct Aes128;
|
||||
|
||||
/// Marker type for AES-192
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
pub struct Aes192;
|
||||
|
||||
/// Marker type for AES-256
|
||||
pub struct Aes256;
|
||||
|
||||
/// Block cipher
|
||||
pub struct Cipher<'a, 'd, T: AesFlavour> {
|
||||
aes: &'a mut Aes<'d>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: AesFlavour> Cipher<'a, 'd, T> {
|
||||
/// Creates and returns a new cipher
|
||||
pub fn new(aes: &'a mut Aes<'d>, key: &Key<T>) -> Self {
|
||||
aes.write_key(key.key);
|
||||
Self {
|
||||
aes,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Encrypts the given buffer
|
||||
pub fn encrypt_block(&mut self, block: &mut [u8; 16]) {
|
||||
self.set_mode(T::ENCRYPT_MODE);
|
||||
self.set_block(block);
|
||||
self.start();
|
||||
while !(self.is_idle()) {}
|
||||
self.get_block(block);
|
||||
}
|
||||
|
||||
/// Decrypts the given buffer
|
||||
pub fn decrypt_block(&mut self, block: &mut [u8; 16]) {
|
||||
self.set_mode(T::DECRYPT_MODE);
|
||||
self.set_block(block);
|
||||
self.start();
|
||||
while !(self.is_idle()) {}
|
||||
self.get_block(block);
|
||||
}
|
||||
|
||||
fn set_mode(&mut self, mode: u32) {
|
||||
self.aes.write_mode(mode);
|
||||
}
|
||||
|
||||
fn is_idle(&mut self) -> bool {
|
||||
self.aes.read_idle()
|
||||
}
|
||||
|
||||
fn set_block(&mut self, block: &[u8; 16]) {
|
||||
self.aes.write_block(block);
|
||||
}
|
||||
|
||||
fn get_block(&self, block: &mut [u8; 16]) {
|
||||
self.aes.read_block(block);
|
||||
}
|
||||
|
||||
fn start(&mut self) {
|
||||
self.aes.write_start();
|
||||
}
|
||||
}
|
||||
|
||||
/// Aes cipher key
|
||||
///
|
||||
/// A `Key` can be initialized from an array of appropriate length:
|
||||
///
|
||||
/// ``` plain
|
||||
/// let key = Key::<Aes128>::from(&[0_u8;16]);
|
||||
/// let key = Key::<Aes192>::from(&[0_u8;24]);
|
||||
/// let key = Key::<Aes256>::from(&[0_u8;32]);
|
||||
/// ```
|
||||
pub struct Key<'b, T: AesFlavour> {
|
||||
key: &'b [u8],
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'b, T, const N: usize> From<&'b [u8; N]> for Key<'b, T>
|
||||
where
|
||||
T: AesFlavour<KeyType<'b> = &'b [u8; N]>,
|
||||
{
|
||||
fn from(value: T::KeyType<'b>) -> Self {
|
||||
Key {
|
||||
key: value,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// State matrix endianness
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
pub enum Endianness {
|
||||
BigEndian = 1,
|
||||
LittleEndian = 0,
|
||||
}
|
||||
@ -1,477 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embedded_hal::adc::{Channel, OneShot};
|
||||
|
||||
use crate::{
|
||||
analog::{ADC1, ADC2},
|
||||
peripheral::PeripheralRef,
|
||||
peripherals::{RTC_IO, SENS},
|
||||
};
|
||||
|
||||
/// The sampling/readout resolution of the ADC
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Resolution {
|
||||
Resolution9Bit = 0b00,
|
||||
Resolution10Bit = 0b01,
|
||||
Resolution11Bit = 0b10,
|
||||
Resolution12Bit = 0b11,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Attenuation {
|
||||
Attenuation0dB = 0b00,
|
||||
Attenuation2p5dB = 0b01,
|
||||
Attenuation6dB = 0b10,
|
||||
Attenuation11dB = 0b11,
|
||||
}
|
||||
|
||||
pub struct AdcPin<PIN, ADCI> {
|
||||
pub pin: PIN,
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<PIN: Channel<ADCI, ID = u8>, ADCI> Channel<ADCI> for AdcPin<PIN, ADCI> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
PIN::channel()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdcConfig<ADCI> {
|
||||
pub resolution: Resolution,
|
||||
pub attenuations: [Option<Attenuation>; 10],
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<ADCI> AdcConfig<ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn new() -> AdcConfig<ADCI> {
|
||||
crate::into_ref!();
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI> {
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
pin,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADCI> Default for AdcConfig<ADCI> {
|
||||
fn default() -> Self {
|
||||
AdcConfig {
|
||||
resolution: Resolution::Resolution12Bit,
|
||||
attenuations: [None; 10],
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RegisterAccess {
|
||||
fn set_bit_width(resolution: u8);
|
||||
|
||||
fn set_sample_bit(resolution: u8);
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8);
|
||||
|
||||
fn clear_dig_force();
|
||||
|
||||
fn set_start_force();
|
||||
|
||||
fn set_en_pad_force();
|
||||
|
||||
fn set_en_pad(channel: u8);
|
||||
|
||||
fn clear_start_sar();
|
||||
|
||||
fn set_start_sar();
|
||||
|
||||
fn read_done_sar() -> bool;
|
||||
|
||||
fn read_data_sar() -> u16;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl RegisterAccess for ADC1 {
|
||||
fn set_bit_width(resolution: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_start_force
|
||||
.modify(|_, w| unsafe { w.sar1_bit_width().bits(resolution) });
|
||||
}
|
||||
|
||||
fn set_sample_bit(resolution: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_read_ctrl
|
||||
.modify(|_, w| unsafe { w.sar1_sample_bit().bits(resolution) });
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_atten1.modify(|r, w| {
|
||||
let new_value = (r.bits() & !(0b11 << (channel * 2)))
|
||||
| (((attenuation as u8 & 0b11) as u32) << (channel * 2));
|
||||
|
||||
unsafe { w.sar1_atten().bits(new_value) }
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_dig_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_read_ctrl
|
||||
.modify(|_, w| w.sar1_dig_force().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start1
|
||||
.modify(|_, w| w.meas1_start_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start1
|
||||
.modify(|_, w| w.sar1_en_pad_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad(channel: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start1
|
||||
.modify(|_, w| unsafe { w.sar1_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start1
|
||||
.modify(|_, w| w.meas1_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start1
|
||||
.modify(|_, w| w.meas1_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas_start1.read().meas1_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas_start1.read().meas1_data_sar().bits() as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC2 {
|
||||
fn set_bit_width(resolution: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_start_force
|
||||
.modify(|_, w| unsafe { w.sar2_bit_width().bits(resolution) });
|
||||
}
|
||||
|
||||
fn set_sample_bit(resolution: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_read_ctrl2
|
||||
.modify(|_, w| unsafe { w.sar2_sample_bit().bits(resolution) });
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_atten2.modify(|r, w| {
|
||||
let new_value = (r.bits() & !(0b11 << (channel * 2)))
|
||||
| (((attenuation as u8 & 0b11) as u32) << (channel * 2));
|
||||
|
||||
unsafe { w.sar2_atten().bits(new_value) }
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_dig_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_read_ctrl2
|
||||
.modify(|_, w| w.sar2_dig_force().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start2
|
||||
.modify(|_, w| w.meas2_start_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start2
|
||||
.modify(|_, w| w.sar2_en_pad_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad(channel: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start2
|
||||
.modify(|_, w| unsafe { w.sar2_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start2
|
||||
.modify(|_, w| w.meas2_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas_start2
|
||||
.modify(|_, w| w.meas2_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas_start2.read().meas2_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas_start2.read().meas2_data_sar().bits() as u16
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC<'d, ADC> {
|
||||
_adc: PeripheralRef<'d, ADC>,
|
||||
attenuations: [Option<Attenuation>; 10],
|
||||
active_channel: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'d, ADCI> ADC<'d, ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn adc(
|
||||
adc_instance: impl crate::peripheral::Peripheral<P = ADCI> + 'd,
|
||||
config: AdcConfig<ADCI>,
|
||||
) -> Result<Self, ()> {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
// Set reading and sampling resolution
|
||||
let resolution: u8 = config.resolution as u8;
|
||||
|
||||
ADCI::set_bit_width(resolution);
|
||||
ADCI::set_sample_bit(resolution);
|
||||
|
||||
// Set attenuation for pins
|
||||
let attenuations = config.attenuations;
|
||||
|
||||
for channel in 0..attenuations.len() {
|
||||
if let Some(attenuation) = attenuations[channel] {
|
||||
ADC1::set_attenuation(channel, attenuation as u8);
|
||||
}
|
||||
}
|
||||
|
||||
// Set controller to RTC
|
||||
ADCI::clear_dig_force();
|
||||
ADCI::set_start_force();
|
||||
ADCI::set_en_pad_force();
|
||||
sensors
|
||||
.sar_touch_ctrl1
|
||||
.modify(|_, w| w.xpd_hall_force().set_bit());
|
||||
sensors
|
||||
.sar_touch_ctrl1
|
||||
.modify(|_, w| w.hall_phase_force().set_bit());
|
||||
|
||||
// Set power to SW power on
|
||||
sensors
|
||||
.sar_meas_wait2
|
||||
.modify(|_, w| unsafe { w.force_xpd_sar().bits(0b11) });
|
||||
|
||||
// disable AMP
|
||||
sensors
|
||||
.sar_meas_wait2
|
||||
.modify(|_, w| unsafe { w.force_xpd_amp().bits(0b10) });
|
||||
sensors
|
||||
.sar_meas_ctrl
|
||||
.modify(|_, w| unsafe { w.amp_rst_fb_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_meas_ctrl
|
||||
.modify(|_, w| unsafe { w.amp_short_ref_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_meas_ctrl
|
||||
.modify(|_, w| unsafe { w.amp_short_ref_gnd_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_meas_wait1
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait1().bits(1) });
|
||||
sensors
|
||||
.sar_meas_wait1
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait2().bits(1) });
|
||||
sensors
|
||||
.sar_meas_wait2
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait3().bits(1) });
|
||||
|
||||
let adc = ADC {
|
||||
_adc: adc_instance.into_ref(),
|
||||
attenuations: config.attenuations,
|
||||
active_channel: None,
|
||||
};
|
||||
|
||||
Ok(adc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADC1> ADC<'d, ADC1> {
|
||||
pub fn enable_hall_sensor() {
|
||||
// Connect hall sensor
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
rtcio.hall_sens.modify(|_, w| w.xpd_hall().set_bit());
|
||||
}
|
||||
|
||||
pub fn disable_hall_sensor() {
|
||||
// Disconnect hall sensor
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
rtcio.hall_sens.modify(|_, w| w.xpd_hall().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADCI, WORD, PIN> OneShot<ADCI, WORD, AdcPin<PIN, ADCI>> for ADC<'d, ADCI>
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self, _pin: &mut AdcPin<PIN, ADCI>) -> nb::Result<WORD, Self::Error> {
|
||||
if self.attenuations[AdcPin::<PIN, ADCI>::channel() as usize] == None {
|
||||
panic!(
|
||||
"Channel {} is not configured reading!",
|
||||
AdcPin::<PIN, ADCI>::channel()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(active_channel) = self.active_channel {
|
||||
// There is conversion in progress:
|
||||
// - if it's for a different channel try again later
|
||||
// - if it's for the given channel, go ahead and check progress
|
||||
if active_channel != AdcPin::<PIN, ADCI>::channel() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
} else {
|
||||
// If no conversions are in progress, start a new one for given channel
|
||||
self.active_channel = Some(AdcPin::<PIN, ADCI>::channel());
|
||||
|
||||
ADCI::set_en_pad(AdcPin::<PIN, ADCI>::channel() as u8);
|
||||
|
||||
ADCI::clear_start_sar();
|
||||
ADCI::set_start_sar();
|
||||
}
|
||||
|
||||
// Wait for ADC to finish conversion
|
||||
let conversion_finished = ADCI::read_done_sar();
|
||||
if !conversion_finished {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
let converted_value = ADCI::read_data_sar();
|
||||
|
||||
// Mark that no conversions are currently in progress
|
||||
self.active_channel = None;
|
||||
|
||||
Ok(converted_value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_adc_interface {
|
||||
($adc:ident [
|
||||
$( ($pin:ident, $channel:expr) ,)+
|
||||
]) => {
|
||||
|
||||
$(
|
||||
impl Channel<$adc> for $pin<Analog> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> u8 { $channel }
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub use impl_adc_interface;
|
||||
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from two
|
||||
//! analog to digital converters available on the ESP32: `ADC1` and `ADC2`.
|
||||
//!
|
||||
//! The following pins can be configured for analog readout:
|
||||
//!
|
||||
//! | Channel | ADC1 | ADC2 |
|
||||
//! |---------|----------------------|---------------|
|
||||
//! | 0 | GPIO36 (SENSOR_VP) | GPIO4 |
|
||||
//! | 1 | GPIO37 (SENSOR_CAPP) | GPIO0 |
|
||||
//! | 2 | GPIO38 (SENSOR_CAPN) | GPIO2 |
|
||||
//! | 3 | GPIO39 (SENSOR_VN) | GPIO15 (MTDO) |
|
||||
//! | 4 | GPIO33 (32K_XP) | GPIO13 (MTCK) |
|
||||
//! | 5 | GPIO32 (32K_XN) | GPIO12 (MTDI) |
|
||||
//! | 6 | GPIO34 (VDET_1) | GPIO14 (MTMS) |
|
||||
//! | 7 | GPIO35 (VDET_2) | GPIO27 |
|
||||
//! | 8 | | GPIO25 |
|
||||
//! | 9 | | GPIO26 |
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
use super::impl_adc_interface;
|
||||
pub use crate::analog::{adc::*, ADC1, ADC2};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio36, 0), // Alt. name: SENSOR_VP
|
||||
(Gpio37, 1), // Alt. name: SENSOR_CAPP
|
||||
(Gpio38, 2), // Alt. name: SENSOR_CAPN
|
||||
(Gpio39, 3), // Alt. name: SENSOR_VN
|
||||
(Gpio33, 4), // Alt. name: 32K_XP
|
||||
(Gpio32, 5), // Alt. name: 32K_XN
|
||||
(Gpio34, 6), // Alt. name: VDET_1
|
||||
(Gpio35, 7), // Alt. name: VDET_2
|
||||
]
|
||||
}
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC2 [
|
||||
(Gpio4, 0),
|
||||
(Gpio0, 1),
|
||||
(Gpio2, 2),
|
||||
(Gpio15, 3), // Alt. name: MTDO
|
||||
(Gpio13, 4), // Alt. name: MTCK
|
||||
(Gpio12, 5), // Alt. name: MTDI
|
||||
(Gpio14, 6), // Alt. name: MTMS
|
||||
(Gpio27, 7),
|
||||
(Gpio25, 8),
|
||||
(Gpio26, 9),
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,389 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embedded_hal::adc::{Channel, OneShot};
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
use crate::analog::ADC2;
|
||||
use crate::{
|
||||
analog::ADC1,
|
||||
peripheral::PeripheralRef,
|
||||
peripherals::APB_SARADC,
|
||||
system::{Peripheral, PeripheralClockControl},
|
||||
};
|
||||
|
||||
/// The sampling/readout resolution of the ADC
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Resolution {
|
||||
Resolution12Bit,
|
||||
}
|
||||
|
||||
/// The attenuation of the ADC pin
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Attenuation {
|
||||
Attenuation0dB = 0b00,
|
||||
Attenuation2p5dB = 0b01,
|
||||
Attenuation6dB = 0b10,
|
||||
Attenuation11dB = 0b11,
|
||||
}
|
||||
|
||||
pub struct AdcPin<PIN, ADCI> {
|
||||
pub pin: PIN,
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<PIN: Channel<ADCI, ID = u8>, ADCI> Channel<ADCI> for AdcPin<PIN, ADCI> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
PIN::channel()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdcConfig<ADCI> {
|
||||
pub resolution: Resolution,
|
||||
pub attenuations: [Option<Attenuation>; 5],
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<ADCI> AdcConfig<ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn new() -> AdcConfig<ADCI> {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI> {
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
pin,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADCI> Default for AdcConfig<ADCI> {
|
||||
fn default() -> Self {
|
||||
AdcConfig {
|
||||
resolution: Resolution::Resolution12Bit,
|
||||
attenuations: [None; 5],
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait RegisterAccess {
|
||||
fn start_onetime_sample(channel: u8, attenuation: u8);
|
||||
|
||||
fn is_done() -> bool;
|
||||
|
||||
fn read_data() -> u16;
|
||||
|
||||
fn reset();
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC1 {
|
||||
fn start_onetime_sample(channel: u8, attenuation: u8) {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc.onetime_sample.modify(|_, w| unsafe {
|
||||
w.saradc1_onetime_sample()
|
||||
.set_bit()
|
||||
.saradc_onetime_channel()
|
||||
.bits(channel)
|
||||
.saradc_onetime_atten()
|
||||
.bits(attenuation)
|
||||
.saradc_onetime_start()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn is_done() -> bool {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc.int_raw.read().apb_saradc1_done_int_raw().bit()
|
||||
}
|
||||
|
||||
fn read_data() -> u16 {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
(sar_adc.sar1data_status.read().apb_saradc1_data().bits() as u16) & 0xfff
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc
|
||||
.int_clr
|
||||
.write(|w| w.apb_saradc1_done_int_clr().set_bit());
|
||||
|
||||
sar_adc
|
||||
.onetime_sample
|
||||
.modify(|_, w| w.saradc_onetime_start().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
impl RegisterAccess for ADC2 {
|
||||
fn start_onetime_sample(channel: u8, attenuation: u8) {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc.onetime_sample.modify(|_, w| unsafe {
|
||||
w.saradc2_onetime_sample()
|
||||
.set_bit()
|
||||
.saradc_onetime_channel()
|
||||
.bits(channel)
|
||||
.saradc_onetime_atten()
|
||||
.bits(attenuation)
|
||||
.saradc_onetime_start()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn is_done() -> bool {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc.int_raw.read().apb_saradc2_done_int_raw().bit()
|
||||
}
|
||||
|
||||
fn read_data() -> u16 {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
(sar_adc.sar2data_status.read().apb_saradc2_data().bits() as u16) & 0xfff
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
|
||||
sar_adc
|
||||
.int_clr
|
||||
.write(|w| w.apb_saradc2_done_int_clr().set_bit());
|
||||
|
||||
sar_adc
|
||||
.onetime_sample
|
||||
.modify(|_, w| w.saradc_onetime_start().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC<'d, ADCI> {
|
||||
_adc: PeripheralRef<'d, ADCI>,
|
||||
attenuations: [Option<Attenuation>; 5],
|
||||
active_channel: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'d, ADCI> ADC<'d, ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn adc(
|
||||
peripheral_clock_controller: &mut PeripheralClockControl,
|
||||
adc_instance: impl crate::peripheral::Peripheral<P = ADCI> + 'd,
|
||||
config: AdcConfig<ADCI>,
|
||||
) -> Result<Self, ()> {
|
||||
peripheral_clock_controller.enable(Peripheral::ApbSarAdc);
|
||||
|
||||
let sar_adc = unsafe { &*APB_SARADC::PTR };
|
||||
sar_adc.ctrl.modify(|_, w| unsafe {
|
||||
w.saradc_start_force()
|
||||
.set_bit()
|
||||
.saradc_start()
|
||||
.set_bit()
|
||||
.saradc_sar_clk_gated()
|
||||
.set_bit()
|
||||
.saradc_xpd_sar_force()
|
||||
.bits(0b11)
|
||||
});
|
||||
let adc = ADC {
|
||||
_adc: adc_instance.into_ref(),
|
||||
attenuations: config.attenuations,
|
||||
active_channel: None,
|
||||
};
|
||||
|
||||
Ok(adc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADCI, WORD, PIN> OneShot<ADCI, WORD, AdcPin<PIN, ADCI>> for ADC<'d, ADCI>
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self, _pin: &mut AdcPin<PIN, ADCI>) -> nb::Result<WORD, Self::Error> {
|
||||
if self.attenuations[AdcPin::<PIN, ADCI>::channel() as usize] == None {
|
||||
panic!(
|
||||
"Channel {} is not configured reading!",
|
||||
AdcPin::<PIN, ADCI>::channel()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(active_channel) = self.active_channel {
|
||||
// There is conversion in progress:
|
||||
// - if it's for a different channel try again later
|
||||
// - if it's for the given channel, go ahead and check progress
|
||||
if active_channel != AdcPin::<PIN, ADCI>::channel() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
} else {
|
||||
// If no conversions are in progress, start a new one for given channel
|
||||
self.active_channel = Some(AdcPin::<PIN, ADCI>::channel());
|
||||
|
||||
let channel = self.active_channel.unwrap();
|
||||
let attenuation = self.attenuations[channel as usize].unwrap() as u8;
|
||||
ADCI::start_onetime_sample(channel, attenuation);
|
||||
}
|
||||
|
||||
// Wait for ADC to finish conversion
|
||||
let conversion_finished = ADCI::is_done();
|
||||
if !conversion_finished {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
let converted_value = ADCI::read_data();
|
||||
ADCI::reset();
|
||||
|
||||
// There is a hardware limitation. If the APB clock frequency is high, the step
|
||||
// of this reg signal: ``onetime_start`` may not be captured by the
|
||||
// ADC digital controller (when its clock frequency is too slow). A rough
|
||||
// estimate for this step should be at least 3 ADC digital controller
|
||||
// clock cycle.
|
||||
//
|
||||
// This limitation will be removed in hardware future versions.
|
||||
// We reset ``onetime_start`` in `reset` and assume enough time has passed until
|
||||
// the next sample is requested.
|
||||
|
||||
// Mark that no conversions are currently in progress
|
||||
self.active_channel = None;
|
||||
|
||||
Ok(converted_value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_adc_interface {
|
||||
($adc:ident [
|
||||
$( ($pin:ident, $channel:expr) ,)+
|
||||
]) => {
|
||||
|
||||
$(
|
||||
impl Channel<$adc> for $pin<Analog> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> u8 { $channel }
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub use impl_adc_interface;
|
||||
|
||||
#[cfg(esp32c2)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from the
|
||||
//! analog to digital converter available on the ESP32-C2: `ADC1`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
pub use crate::analog::{adc::*, ADC1};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio0, 0),
|
||||
(Gpio1, 1),
|
||||
(Gpio2, 2),
|
||||
(Gpio3, 3),
|
||||
(Gpio4, 4),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from two
|
||||
//! analog to digital converters available on the ESP32-C3: `ADC1` and
|
||||
//! `ADC2`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
pub use crate::analog::{adc::*, ADC1, ADC2};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio0, 0),
|
||||
(Gpio1, 1),
|
||||
(Gpio2, 2),
|
||||
(Gpio3, 3),
|
||||
(Gpio4, 4),
|
||||
]
|
||||
}
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC2 [
|
||||
(Gpio5, 0),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from one
|
||||
//! analog to digital converter available on the ESP32-C6: `ADC1`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
pub use crate::analog::{adc::*, ADC1};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio0, 0),
|
||||
(Gpio1, 1),
|
||||
(Gpio2, 2),
|
||||
(Gpio3, 3),
|
||||
(Gpio4, 4),
|
||||
(Gpio5, 5),
|
||||
(Gpio6, 6),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32h2)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from one
|
||||
//! analog to digital converter available on the ESP32-H2: `ADC1`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
pub use crate::analog::{adc::*, ADC1};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio1, 0),
|
||||
(Gpio2, 1),
|
||||
(Gpio3, 2),
|
||||
(Gpio4, 3),
|
||||
(Gpio5, 4),
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,501 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embedded_hal::adc::{Channel, OneShot};
|
||||
|
||||
use crate::{
|
||||
analog::{ADC1, ADC2},
|
||||
peripheral::PeripheralRef,
|
||||
peripherals::{APB_SARADC, SENS},
|
||||
};
|
||||
|
||||
/// The sampling/readout resolution of the ADC
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Resolution {
|
||||
Resolution13Bit,
|
||||
}
|
||||
|
||||
/// The attenuation of the ADC pin
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Attenuation {
|
||||
Attenuation0dB = 0b00,
|
||||
Attenuation2p5dB = 0b01,
|
||||
Attenuation6dB = 0b10,
|
||||
Attenuation11dB = 0b11,
|
||||
}
|
||||
|
||||
pub struct AdcPin<PIN, ADCI> {
|
||||
pub pin: PIN,
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<PIN: Channel<ADCI, ID = u8>, ADCI> Channel<ADCI> for AdcPin<PIN, ADCI> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> Self::ID {
|
||||
PIN::channel()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdcConfig<ADCI> {
|
||||
pub resolution: Resolution,
|
||||
pub attenuations: [Option<Attenuation>; 10],
|
||||
_phantom: PhantomData<ADCI>,
|
||||
}
|
||||
|
||||
impl<ADCI> AdcConfig<ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn new() -> AdcConfig<ADCI> {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enable_pin<PIN: Channel<ADCI, ID = u8>>(
|
||||
&mut self,
|
||||
pin: PIN,
|
||||
attenuation: Attenuation,
|
||||
) -> AdcPin<PIN, ADCI> {
|
||||
self.attenuations[PIN::channel() as usize] = Some(attenuation);
|
||||
|
||||
AdcPin {
|
||||
pin,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ADCI> Default for AdcConfig<ADCI> {
|
||||
fn default() -> Self {
|
||||
AdcConfig {
|
||||
resolution: Resolution::Resolution13Bit,
|
||||
attenuations: [None; 10],
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait RegisterAccess {
|
||||
fn set_bit_width(resolution: u8);
|
||||
|
||||
fn set_sample_bit(resolution: u8);
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8);
|
||||
|
||||
fn clear_dig_force();
|
||||
|
||||
fn set_start_force();
|
||||
|
||||
fn set_en_pad_force();
|
||||
|
||||
fn set_en_pad(channel: u8);
|
||||
|
||||
fn clear_start_sar();
|
||||
|
||||
fn set_start_sar();
|
||||
|
||||
fn read_done_sar() -> bool;
|
||||
|
||||
fn read_data_sar() -> u16;
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC1 {
|
||||
fn set_bit_width(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_sample_bit(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_atten1.modify(|r, w| {
|
||||
let new_value = (r.bits() & !(0b11 << (channel * 2)))
|
||||
| (((attenuation as u8 & 0b11) as u32) << (channel * 2));
|
||||
|
||||
unsafe { w.sar1_atten().bits(new_value) }
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_dig_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_mux
|
||||
.modify(|_, w| w.sar1_dig_force().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.sar1_en_pad_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad(channel: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| unsafe { w.sar1_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas1_ctrl2
|
||||
.modify(|_, w| w.meas1_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas1_ctrl2.read().meas1_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas1_ctrl2.read().meas1_data_sar().bits() as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for ADC2 {
|
||||
fn set_bit_width(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_sample_bit(_resolution: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_attenuation(channel: usize, attenuation: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_atten2.modify(|r, w| {
|
||||
let new_value = (r.bits() & !(0b11 << (channel * 2)))
|
||||
| (((attenuation as u8 & 0b11) as u32) << (channel * 2));
|
||||
|
||||
unsafe { w.sar2_atten().bits(new_value) }
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_dig_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_mux
|
||||
.modify(|_, w| w.sar2_rtc_force().set_bit());
|
||||
|
||||
let sar_apb = unsafe { &*APB_SARADC::ptr() };
|
||||
sar_apb
|
||||
.arb_ctrl
|
||||
.modify(|_, w| w.adc_arb_rtc_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_start_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad_force() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.sar2_en_pad_force().set_bit());
|
||||
}
|
||||
|
||||
fn set_en_pad(channel: u8) {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| unsafe { w.sar2_en_pad().bits(1 << channel) });
|
||||
}
|
||||
|
||||
fn clear_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_sar().clear_bit());
|
||||
}
|
||||
|
||||
fn set_start_sar() {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_meas2_ctrl2
|
||||
.modify(|_, w| w.meas2_start_sar().set_bit());
|
||||
}
|
||||
|
||||
fn read_done_sar() -> bool {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas2_ctrl2.read().meas2_done_sar().bit_is_set()
|
||||
}
|
||||
|
||||
fn read_data_sar() -> u16 {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors.sar_meas2_ctrl2.read().meas2_data_sar().bits() as u16
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ADC<'d, ADC> {
|
||||
_adc: PeripheralRef<'d, ADC>,
|
||||
attenuations: [Option<Attenuation>; 10],
|
||||
active_channel: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'d, ADCI> ADC<'d, ADCI>
|
||||
where
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
pub fn adc(
|
||||
adc_instance: impl crate::peripheral::Peripheral<P = ADCI> + 'd,
|
||||
config: AdcConfig<ADCI>,
|
||||
) -> Result<Self, ()> {
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
|
||||
// Set reading and sampling resolution
|
||||
let resolution: u8 = config.resolution as u8;
|
||||
|
||||
ADCI::set_bit_width(resolution);
|
||||
ADCI::set_sample_bit(resolution);
|
||||
|
||||
// Set attenuation for pins
|
||||
let attenuations = config.attenuations;
|
||||
|
||||
for channel in 0..attenuations.len() {
|
||||
if let Some(attenuation) = attenuations[channel] {
|
||||
ADCI::set_attenuation(channel, attenuation as u8);
|
||||
}
|
||||
}
|
||||
|
||||
// Set controller to RTC
|
||||
ADCI::clear_dig_force();
|
||||
ADCI::set_start_force();
|
||||
ADCI::set_en_pad_force();
|
||||
sensors
|
||||
.sar_hall_ctrl
|
||||
.modify(|_, w| w.xpd_hall_force().set_bit());
|
||||
sensors
|
||||
.sar_hall_ctrl
|
||||
.modify(|_, w| w.hall_phase_force().set_bit());
|
||||
|
||||
// Set power to SW power on
|
||||
#[cfg(esp32s2)]
|
||||
sensors
|
||||
.sar_meas1_ctrl1
|
||||
.modify(|_, w| w.rtc_saradc_clkgate_en().set_bit());
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
sensors
|
||||
.sar_peri_clk_gate_conf
|
||||
.modify(|_, w| w.saradc_clk_en().set_bit());
|
||||
|
||||
sensors
|
||||
.sar_power_xpd_sar
|
||||
.modify(|_, w| w.sarclk_en().set_bit());
|
||||
|
||||
sensors
|
||||
.sar_power_xpd_sar
|
||||
.modify(|_, w| unsafe { w.force_xpd_sar().bits(0b11) });
|
||||
|
||||
// disable AMP
|
||||
sensors
|
||||
.sar_meas1_ctrl1
|
||||
.modify(|_, w| unsafe { w.force_xpd_amp().bits(0b11) });
|
||||
sensors
|
||||
.sar_amp_ctrl3
|
||||
.modify(|_, w| unsafe { w.amp_rst_fb_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_amp_ctrl3
|
||||
.modify(|_, w| unsafe { w.amp_short_ref_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_amp_ctrl3
|
||||
.modify(|_, w| unsafe { w.amp_short_ref_gnd_fsm().bits(0) });
|
||||
sensors
|
||||
.sar_amp_ctrl1
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait1().bits(1) });
|
||||
sensors
|
||||
.sar_amp_ctrl1
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait2().bits(1) });
|
||||
sensors
|
||||
.sar_amp_ctrl2
|
||||
.modify(|_, w| unsafe { w.sar_amp_wait3().bits(1) });
|
||||
|
||||
let adc = ADC {
|
||||
_adc: adc_instance.into_ref(),
|
||||
attenuations: config.attenuations,
|
||||
active_channel: None,
|
||||
};
|
||||
|
||||
Ok(adc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, ADCI, WORD, PIN> OneShot<ADCI, WORD, AdcPin<PIN, ADCI>> for ADC<'d, ADCI>
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<ADCI, ID = u8>,
|
||||
ADCI: RegisterAccess,
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn read(&mut self, _pin: &mut AdcPin<PIN, ADCI>) -> nb::Result<WORD, Self::Error> {
|
||||
if self.attenuations[AdcPin::<PIN, ADCI>::channel() as usize] == None {
|
||||
panic!(
|
||||
"Channel {} is not configured reading!",
|
||||
AdcPin::<PIN, ADCI>::channel()
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(active_channel) = self.active_channel {
|
||||
// There is conversion in progress:
|
||||
// - if it's for a different channel try again later
|
||||
// - if it's for the given channel, go ahead and check progress
|
||||
if active_channel != AdcPin::<PIN, ADCI>::channel() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
} else {
|
||||
// If no conversions are in progress, start a new one for given channel
|
||||
self.active_channel = Some(AdcPin::<PIN, ADCI>::channel());
|
||||
|
||||
ADCI::set_en_pad(AdcPin::<PIN, ADCI>::channel() as u8);
|
||||
|
||||
ADCI::clear_start_sar();
|
||||
ADCI::set_start_sar();
|
||||
}
|
||||
|
||||
// Wait for ADC to finish conversion
|
||||
let conversion_finished = ADCI::read_done_sar();
|
||||
if !conversion_finished {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
let converted_value = ADCI::read_data_sar();
|
||||
|
||||
// Mark that no conversions are currently in progress
|
||||
self.active_channel = None;
|
||||
|
||||
Ok(converted_value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_adc_interface {
|
||||
($adc:ident [
|
||||
$( ($pin:ident, $channel:expr) ,)+
|
||||
]) => {
|
||||
|
||||
$(
|
||||
impl Channel<$adc> for $pin<Analog> {
|
||||
type ID = u8;
|
||||
|
||||
fn channel() -> u8 { $channel }
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub use impl_adc_interface;
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from two
|
||||
//! analog to digital converters available on the ESP32-S3: `ADC1` and
|
||||
//! `ADC2`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
use super::impl_adc_interface;
|
||||
pub use crate::analog::{adc::*, ADC1, ADC2};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio1, 0),
|
||||
(Gpio2, 1),
|
||||
(Gpio3, 2),
|
||||
(Gpio4, 3),
|
||||
(Gpio5, 4),
|
||||
(Gpio6, 5),
|
||||
(Gpio7, 6),
|
||||
(Gpio8, 7),
|
||||
(Gpio9, 8),
|
||||
(Gpio10,9),
|
||||
]
|
||||
}
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC2 [
|
||||
(Gpio11, 0),
|
||||
(Gpio12, 1),
|
||||
(Gpio13, 2),
|
||||
(Gpio14, 3),
|
||||
(Gpio15, 4),
|
||||
(Gpio16, 5),
|
||||
(Gpio17, 6),
|
||||
(Gpio18, 7),
|
||||
(Gpio19, 8),
|
||||
(Gpio20, 9),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
pub mod implementation {
|
||||
//! Analog to digital (ADC) conversion support.
|
||||
//!
|
||||
//! This module provides functions for reading analog values from two
|
||||
//! analog to digital converters available on the ESP32-S2: `ADC1` and
|
||||
//! `ADC2`.
|
||||
|
||||
use embedded_hal::adc::Channel;
|
||||
|
||||
use super::impl_adc_interface;
|
||||
pub use crate::analog::{adc::*, ADC1, ADC2};
|
||||
use crate::gpio::*;
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC1 [
|
||||
(Gpio1, 0),
|
||||
(Gpio2, 1),
|
||||
(Gpio3, 2),
|
||||
(Gpio4, 3),
|
||||
(Gpio5, 4),
|
||||
(Gpio6, 5),
|
||||
(Gpio7, 6),
|
||||
(Gpio8, 7),
|
||||
(Gpio9, 8),
|
||||
(Gpio10,9),
|
||||
]
|
||||
}
|
||||
|
||||
impl_adc_interface! {
|
||||
ADC2 [
|
||||
(Gpio11, 0),
|
||||
(Gpio12, 1),
|
||||
(Gpio13, 2),
|
||||
(Gpio14, 3),
|
||||
(Gpio15, 4),
|
||||
(Gpio16, 5),
|
||||
(Gpio17, 6),
|
||||
(Gpio18, 7),
|
||||
(Gpio19, 8),
|
||||
(Gpio20, 9),
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,161 +0,0 @@
|
||||
use crate::{
|
||||
peripheral::PeripheralRef,
|
||||
peripherals::{RTC_IO, SENS},
|
||||
};
|
||||
pub trait DAC {
|
||||
fn write(&mut self, value: u8);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait DAC1Impl {
|
||||
fn set_power(self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#[cfg(esp32s2)]
|
||||
{
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_dac_ctrl1
|
||||
.modify(|_, w| w.dac_clkgate_en().set_bit());
|
||||
}
|
||||
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
|
||||
rtcio.pad_dac1.modify(|_, w| {
|
||||
w.pdac1_dac_xpd_force().set_bit();
|
||||
w.pdac1_xpd_dac().set_bit()
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn write(&mut self, value: u8) {
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_dac_ctrl2
|
||||
.modify(|_, w| w.dac_cw_en1().clear_bit());
|
||||
|
||||
rtcio
|
||||
.pad_dac1
|
||||
.modify(|_, w| unsafe { w.pdac1_dac().bits(value) });
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait DAC2Impl {
|
||||
fn set_power(self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#[cfg(esp32s2)]
|
||||
{
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_dac_ctrl1
|
||||
.modify(|_, w| w.dac_clkgate_en().set_bit());
|
||||
}
|
||||
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
|
||||
rtcio.pad_dac2.modify(|_, w| {
|
||||
w.pdac2_dac_xpd_force().set_bit();
|
||||
w.pdac2_xpd_dac().set_bit()
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn write(&mut self, value: u8) {
|
||||
let rtcio = unsafe { &*RTC_IO::ptr() };
|
||||
|
||||
let sensors = unsafe { &*SENS::ptr() };
|
||||
sensors
|
||||
.sar_dac_ctrl2
|
||||
.modify(|_, w| w.dac_cw_en2().clear_bit());
|
||||
|
||||
rtcio
|
||||
.pad_dac2
|
||||
.modify(|_, w| unsafe { w.pdac2_dac().bits(value) });
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! impl_dac {
|
||||
($($number:literal => $gpio:ident,)+) => {
|
||||
use core::marker::PhantomData;
|
||||
use crate::gpio;
|
||||
|
||||
$(
|
||||
paste::paste! {
|
||||
pub use $crate::analog::dac::[<DAC $number Impl>];
|
||||
|
||||
/// DAC channel
|
||||
pub struct [<DAC $number>]<'d, DAC> {
|
||||
_dac: PeripheralRef<'d, DAC>,
|
||||
_private: PhantomData<()>,
|
||||
}
|
||||
|
||||
impl<'d, DAC> [<DAC $number Impl>] for [<DAC $number>]<'d, DAC> {}
|
||||
|
||||
impl<'d, DAC> [<DAC $number>]<'d, DAC> {
|
||||
/// Constructs a new DAC instance
|
||||
pub fn dac(
|
||||
dac: impl $crate::peripheral::Peripheral<P = DAC> +'d,
|
||||
_pin: gpio::$gpio<$crate::gpio::Analog>,
|
||||
) -> Result<Self, ()> {
|
||||
let dac = Self {
|
||||
_dac: dac.into_ref(),
|
||||
_private: PhantomData,
|
||||
}
|
||||
.set_power();
|
||||
Ok(dac)
|
||||
}
|
||||
|
||||
/// Write the given value
|
||||
///
|
||||
/// For each DAC channel, the output analog voltage can be calculated as follows:
|
||||
/// DACn_OUT = VDD3P3_RTC * PDACn_DAC/256
|
||||
pub fn write(&mut self, value: u8) {
|
||||
[<DAC $number Impl>]::write(self, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
pub use impl_dac;
|
||||
|
||||
#[cfg(esp32)]
|
||||
pub mod implementation {
|
||||
//! Digital to analog (DAC) conversion.
|
||||
//!
|
||||
//! This module provides functions for controling two digital to
|
||||
//! analog converters, available on ESP32: `DAC1` and `DAC2`.
|
||||
//!
|
||||
//! The DAC1 is available on the GPIO pin 25, and DAC2 on pin 26.
|
||||
|
||||
pub use super::*;
|
||||
use crate::impl_dac;
|
||||
|
||||
impl_dac!(1 => Gpio25, 2 => Gpio26,);
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
pub mod implementation {
|
||||
//! Digital to analog (DAC) conversion.
|
||||
//!
|
||||
//! This module provides functions for controling two digital to
|
||||
//! analog converters, available on ESP32: `DAC1` and `DAC2`.
|
||||
//!
|
||||
//! The DAC1 is available on the GPIO pin 17, and DAC2 on pin 18.
|
||||
|
||||
pub use super::*;
|
||||
use crate::impl_dac;
|
||||
|
||||
impl_dac!(1 => Gpio17, 2 => Gpio18,);
|
||||
}
|
||||
@ -1,145 +0,0 @@
|
||||
#[cfg_attr(esp32, path = "adc/esp32.rs")]
|
||||
#[cfg_attr(riscv, path = "adc/riscv.rs")]
|
||||
#[cfg_attr(any(esp32s2, esp32s3), path = "adc/xtensa.rs")]
|
||||
pub mod adc;
|
||||
#[cfg(dac)]
|
||||
pub mod dac;
|
||||
|
||||
pub struct ADC1 {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
pub struct ADC2 {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
pub struct DAC1 {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
pub struct DAC2 {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl core::ops::Deref for ADC1 {
|
||||
type Target = ADC1;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for ADC1 {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for ADC1 {
|
||||
type P = ADC1;
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
ADC1 { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for ADC1 {}
|
||||
|
||||
impl crate::peripheral::Peripheral for ADC2 {
|
||||
type P = ADC2;
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
ADC2 { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for ADC2 {}
|
||||
|
||||
impl crate::peripheral::Peripheral for DAC1 {
|
||||
type P = DAC1;
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
DAC1 { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for DAC1 {}
|
||||
|
||||
impl crate::peripheral::Peripheral for DAC2 {
|
||||
type P = DAC2;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
DAC2 { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for DAC2 {}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(xtensa)] {
|
||||
use crate::peripherals::SENS;
|
||||
|
||||
pub struct AvailableAnalog {
|
||||
pub adc1: ADC1,
|
||||
pub adc2: ADC2,
|
||||
pub dac1: DAC1,
|
||||
pub dac2: DAC2,
|
||||
}
|
||||
|
||||
/// Extension trait to split a SENS peripheral in independent parts
|
||||
pub trait SensExt {
|
||||
fn split(self) -> AvailableAnalog;
|
||||
}
|
||||
|
||||
impl SensExt for SENS {
|
||||
fn split(self) -> AvailableAnalog {
|
||||
AvailableAnalog {
|
||||
adc1: ADC1 {
|
||||
_private: (),
|
||||
},
|
||||
adc2: ADC2 {
|
||||
_private: (),
|
||||
},
|
||||
dac1: DAC1 {
|
||||
_private: (),
|
||||
},
|
||||
dac2: DAC2 {
|
||||
_private: (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(riscv)] {
|
||||
use crate::peripherals::APB_SARADC;
|
||||
|
||||
pub struct AvailableAnalog {
|
||||
pub adc1: ADC1,
|
||||
#[cfg(esp32c3)]
|
||||
pub adc2: ADC2,
|
||||
}
|
||||
|
||||
/// Extension trait to split a APB_SARADC peripheral in independent parts
|
||||
pub trait SarAdcExt {
|
||||
fn split(self) -> AvailableAnalog;
|
||||
}
|
||||
|
||||
impl<'d, T: crate::peripheral::Peripheral<P = APB_SARADC> + 'd> SarAdcExt for T {
|
||||
fn split(self) -> AvailableAnalog {
|
||||
AvailableAnalog {
|
||||
adc1: ADC1 {
|
||||
_private: (),
|
||||
},
|
||||
#[cfg(esp32c3)]
|
||||
adc2: ADC2 {
|
||||
_private: (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,350 +0,0 @@
|
||||
use crate::clock::{Clock, PllClock, XtalClock};
|
||||
|
||||
const REF_CLK_FREQ: u32 = 1000000;
|
||||
|
||||
const MHZ: u32 = 1000000;
|
||||
const UINT16_MAX: u32 = 0xffff;
|
||||
|
||||
const RTC_CNTL_DBIAS_1V10: u32 = 4;
|
||||
const RTC_CNTL_DBIAS_1V25: u32 = 7;
|
||||
|
||||
const DIG_DBIAS_80M_160M: u32 = RTC_CNTL_DBIAS_1V10;
|
||||
const DIG_DBIAS_XTAL: u32 = RTC_CNTL_DBIAS_1V10;
|
||||
|
||||
const I2C_BBPLL: u32 = 0x66;
|
||||
const I2C_BBPLL_HOSTID: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_IR_CAL_DELAY: u32 = 0;
|
||||
const I2C_BBPLL_IR_CAL_EXT_CAP: u32 = 1;
|
||||
const I2C_BBPLL_OC_ENB_FCAL: u32 = 4;
|
||||
const I2C_BBPLL_OC_ENB_VCON: u32 = 10;
|
||||
const I2C_BBPLL_BBADC_CAL_7_0: u32 = 12;
|
||||
|
||||
const BBPLL_IR_CAL_DELAY_VAL: u32 = 0x18;
|
||||
const BBPLL_IR_CAL_EXT_CAP_VAL: u32 = 0x20;
|
||||
const BBPLL_OC_ENB_FCAL_VAL: u32 = 0x9a;
|
||||
const BBPLL_OC_ENB_VCON_VAL: u32 = 0x00;
|
||||
const BBPLL_BBADC_CAL_7_0_VAL: u32 = 0x00;
|
||||
|
||||
const I2C_BBPLL_ENDIV5: u32 = 11;
|
||||
|
||||
const BBPLL_ENDIV5_VAL_320M: u32 = 0x43;
|
||||
const BBPLL_BBADC_DSMP_VAL_320M: u32 = 0x84;
|
||||
const BBPLL_ENDIV5_VAL_480M: u32 = 0xc3;
|
||||
const BBPLL_BBADC_DSMP_VAL_480M: u32 = 0x74;
|
||||
|
||||
const I2C_BBPLL_BBADC_DSMP: u32 = 9;
|
||||
const I2C_BBPLL_OC_LREF: u32 = 2;
|
||||
const I2C_BBPLL_OC_DIV_7_0: u32 = 3;
|
||||
const I2C_BBPLL_OC_DCUR: u32 = 5;
|
||||
|
||||
pub(crate) fn esp32_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) {
|
||||
let efuse = unsafe { &*crate::peripherals::EFUSE::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
let rtc_cntl_dbias_hp_volt: u32 =
|
||||
RTC_CNTL_DBIAS_1V25 - efuse.blk0_rdata5.read().rd_vol_level_hp_inv().bits() as u32;
|
||||
let dig_dbias_240_m: u32 = rtc_cntl_dbias_hp_volt;
|
||||
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let div10_8: u32;
|
||||
let lref: u32;
|
||||
let dcur: u32;
|
||||
let bw: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
|
||||
if matches!(pll_freq, PllClock::Pll320MHz) {
|
||||
// Raise the voltage, if needed
|
||||
rtc_cntl
|
||||
.reg
|
||||
.modify(|_, w| w.dig_dbias_wak().variant(DIG_DBIAS_80M_160M as u8));
|
||||
|
||||
// Configure 320M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 32;
|
||||
div10_8 = 0;
|
||||
lref = 0;
|
||||
dcur = 6;
|
||||
bw = 3;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq24M => {
|
||||
div_ref = 11;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 0;
|
||||
dcur = 0;
|
||||
bw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_ENDIV5,
|
||||
BBPLL_ENDIV5_VAL_320M,
|
||||
);
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_BBADC_DSMP,
|
||||
BBPLL_BBADC_DSMP_VAL_320M,
|
||||
);
|
||||
} else {
|
||||
// Raise the voltage
|
||||
rtc_cntl
|
||||
.reg
|
||||
.modify(|_, w| w.dig_dbias_wak().variant(dig_dbias_240_m as u8));
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 28;
|
||||
div10_8 = 0;
|
||||
lref = 0;
|
||||
dcur = 6;
|
||||
bw = 3;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq26M => {
|
||||
div_ref = 12;
|
||||
div7_0 = 144;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq24M => {
|
||||
div_ref = 11;
|
||||
div7_0 = 144;
|
||||
div10_8 = 4;
|
||||
lref = 1;
|
||||
dcur = 0;
|
||||
bw = 1;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 12;
|
||||
div7_0 = 224;
|
||||
div10_8 = 4;
|
||||
lref = 0;
|
||||
dcur = 0;
|
||||
bw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_ENDIV5,
|
||||
BBPLL_ENDIV5_VAL_480M,
|
||||
);
|
||||
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_BBADC_DSMP,
|
||||
BBPLL_BBADC_DSMP_VAL_480M,
|
||||
);
|
||||
}
|
||||
|
||||
i2c_bbpll_lref = (lref << 7) | (div10_8 << 4) | (div_ref);
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur = (bw << 6) | dcur;
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_LREF,
|
||||
i2c_bbpll_lref,
|
||||
);
|
||||
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DIV_7_0,
|
||||
i2c_bbpll_div_7_0,
|
||||
);
|
||||
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DCUR,
|
||||
i2c_bbpll_dcur,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32_rtc_bbpll_enable() {
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
rtc_cntl.options0.modify(|_, w| {
|
||||
w.bias_i2c_force_pd()
|
||||
.clear_bit()
|
||||
.bb_i2c_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
// reset BBPLL configuration
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_IR_CAL_DELAY,
|
||||
BBPLL_IR_CAL_DELAY_VAL,
|
||||
);
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_IR_CAL_EXT_CAP,
|
||||
BBPLL_IR_CAL_EXT_CAP_VAL,
|
||||
);
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_ENB_FCAL,
|
||||
BBPLL_OC_ENB_FCAL_VAL,
|
||||
);
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_ENB_VCON,
|
||||
BBPLL_OC_ENB_VCON_VAL,
|
||||
);
|
||||
i2c_writereg_rtc(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_BBADC_CAL_7_0,
|
||||
BBPLL_BBADC_CAL_7_0_VAL,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn i2c_writereg_rtc(block: u32, block_hostid: u32, reg_add: u32, indata: u32) {
|
||||
const ROM_I2C_WRITEREG: u32 = 0x400041a4;
|
||||
|
||||
// cast to usize is just needed because of the way we run clippy in CI
|
||||
let rom_i2c_writereg: fn(block: u32, block_hostid: u32, reg_add: u32, indata: u32) -> i32 =
|
||||
core::mem::transmute(ROM_I2C_WRITEREG as usize);
|
||||
|
||||
rom_i2c_writereg(block, block_hostid, reg_add, indata);
|
||||
}
|
||||
|
||||
pub(crate) fn esp32_rtc_update_to_xtal(freq: XtalClock, _div: u32) {
|
||||
let apb_cntl = unsafe { &*crate::peripherals::APB_CTRL::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
let value = (((freq.hz()) >> 12) & UINT16_MAX) | ((((freq.hz()) >> 12) & UINT16_MAX) << 16);
|
||||
esp32_update_cpu_freq(freq.hz());
|
||||
// set divider from XTAL to APB clock
|
||||
apb_cntl.sysclk_conf.modify(|_, w| {
|
||||
w.pre_div_cnt()
|
||||
.bits(((freq.hz()) / REF_CLK_FREQ - 1) as u16)
|
||||
});
|
||||
|
||||
// adjust ref_tick
|
||||
apb_cntl.xtal_tick_conf.as_ptr().write_volatile(
|
||||
((freq.hz()) / REF_CLK_FREQ - 1) | apb_cntl.xtal_tick_conf.as_ptr().read_volatile(),
|
||||
); // TODO make it RW in SVD
|
||||
|
||||
// switch clock source
|
||||
rtc_cntl.clk_conf.modify(|_, w| w.soc_clk_sel().xtal());
|
||||
rtc_cntl
|
||||
.store5
|
||||
.modify(|_, w| w.scratch5().bits(value as u32));
|
||||
|
||||
// lower the voltage
|
||||
rtc_cntl
|
||||
.reg
|
||||
.modify(|_, w| w.dig_dbias_wak().variant(DIG_DBIAS_XTAL as u8));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_cpu_freq(cpu_freq_mhz: crate::clock::CpuClock) {
|
||||
let efuse = unsafe { &*crate::peripherals::EFUSE::ptr() };
|
||||
let dport = unsafe { &*crate::peripherals::DPORT::ptr() };
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
unsafe {
|
||||
const RTC_CNTL_DBIAS_1V25: u32 = 7;
|
||||
|
||||
let rtc_cntl_dbias_hp_volt: u32 =
|
||||
RTC_CNTL_DBIAS_1V25 - efuse.blk0_rdata5.read().rd_vol_level_hp_inv().bits() as u32;
|
||||
let dig_dbias_240_m: u32 = rtc_cntl_dbias_hp_volt;
|
||||
|
||||
const CPU_80M: u32 = 0;
|
||||
const CPU_160M: u32 = 1;
|
||||
const CPU_240M: u32 = 2;
|
||||
|
||||
let mut dbias = DIG_DBIAS_80M_160M;
|
||||
let per_conf;
|
||||
|
||||
match cpu_freq_mhz {
|
||||
crate::clock::CpuClock::Clock160MHz => {
|
||||
per_conf = CPU_160M;
|
||||
}
|
||||
crate::clock::CpuClock::Clock240MHz => {
|
||||
dbias = dig_dbias_240_m;
|
||||
per_conf = CPU_240M;
|
||||
}
|
||||
crate::clock::CpuClock::Clock80MHz => {
|
||||
per_conf = CPU_80M;
|
||||
}
|
||||
}
|
||||
|
||||
let value = (((80 * MHZ) >> 12) & UINT16_MAX) | ((((80 * MHZ) >> 12) & UINT16_MAX) << 16);
|
||||
dport
|
||||
.cpu_per_conf
|
||||
.write(|w| w.cpuperiod_sel().bits(per_conf as u8));
|
||||
rtc_cntl
|
||||
.reg
|
||||
.modify(|_, w| w.dig_dbias_wak().variant(dbias as u8));
|
||||
rtc_cntl.clk_conf.modify(|_, w| w.soc_clk_sel().pll());
|
||||
rtc_cntl
|
||||
.store5
|
||||
.modify(|_, w| w.scratch5().bits(value as u32));
|
||||
|
||||
esp32_update_cpu_freq(cpu_freq_mhz.mhz());
|
||||
}
|
||||
}
|
||||
|
||||
/// Pass the CPU clock in MHz so that ets_delay_us
|
||||
/// will be accurate. Call this function when CPU frequency is changed.
|
||||
fn esp32_update_cpu_freq(mhz: u32) {
|
||||
const G_TICKS_PER_US_PRO: u32 = 0x3ffe01e0;
|
||||
unsafe {
|
||||
// Update scale factors used by esp_rom_delay_us
|
||||
(G_TICKS_PER_US_PRO as *mut u32).write_volatile(mhz);
|
||||
}
|
||||
}
|
||||
@ -1,240 +0,0 @@
|
||||
use paste::paste;
|
||||
|
||||
use crate::{
|
||||
clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock},
|
||||
regi2c_write,
|
||||
regi2c_write_mask,
|
||||
rom::{rom_i2c_writeReg, rom_i2c_writeReg_Mask},
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
fn ets_update_cpu_frequency_rom(ticks_per_us: u32);
|
||||
}
|
||||
|
||||
const I2C_BBPLL: u32 = 0x66;
|
||||
const I2C_BBPLL_HOSTID: u32 = 0;
|
||||
|
||||
const I2C_BBPLL_MODE_HF: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_REF_DIV: u32 = 2;
|
||||
const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
|
||||
const I2C_BBPLL_OC_DIV_7_0: u32 = 3;
|
||||
|
||||
const I2C_BBPLL_OC_DR1: u32 = 5;
|
||||
const I2C_BBPLL_OC_DR1_MSB: u32 = 2;
|
||||
const I2C_BBPLL_OC_DR1_LSB: u32 = 0;
|
||||
|
||||
const I2C_BBPLL_OC_DR3: u32 = 5;
|
||||
const I2C_BBPLL_OC_DR3_MSB: u32 = 6;
|
||||
const I2C_BBPLL_OC_DR3_LSB: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_DCUR: u32 = 6;
|
||||
|
||||
const I2C_BBPLL_OC_VCO_DBIAS: u32 = 9;
|
||||
const I2C_BBPLL_OC_VCO_DBIAS_MSB: u32 = 1;
|
||||
const I2C_BBPLL_OC_VCO_DBIAS_LSB: u32 = 0;
|
||||
|
||||
const I2C_BBPLL_OC_DHREF_SEL: u32 = 6;
|
||||
const I2C_BBPLL_OC_DHREF_SEL_MSB: u32 = 5;
|
||||
const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_DLREF_SEL: u32 = 6;
|
||||
const I2C_BBPLL_OC_DLREF_SEL_MSB: u32 = 7;
|
||||
const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;
|
||||
|
||||
const I2C_MST_ANA_CONF0_REG: u32 = 0x6000_e040;
|
||||
const I2C_MST_BBPLL_STOP_FORCE_HIGH: u32 = 1 << 3;
|
||||
const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 2;
|
||||
|
||||
pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) {
|
||||
let system = unsafe { &*crate::peripherals::SYSTEM::ptr() };
|
||||
|
||||
unsafe {
|
||||
let div_ref: u32;
|
||||
let div7_0: u32;
|
||||
let dr1: u32;
|
||||
let dr3: u32;
|
||||
let dchgp: u32;
|
||||
let dcur: u32;
|
||||
let dbias: u32;
|
||||
let i2c_bbpll_lref: u32;
|
||||
let i2c_bbpll_div_7_0: u32;
|
||||
let i2c_bbpll_dcur: u32;
|
||||
|
||||
let clear_reg_mask = |reg, mask: u32| {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask)
|
||||
};
|
||||
let set_reg_mask = |reg, mask: u32| {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | mask)
|
||||
};
|
||||
|
||||
clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH);
|
||||
set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW);
|
||||
|
||||
if matches!(pll_freq, PllClock::Pll480MHz) {
|
||||
// Set this register to let the digital part know 480M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().set_bit());
|
||||
|
||||
// Configure 480M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 26;
|
||||
dr1 = 1;
|
||||
dr3 = 1;
|
||||
dchgp = 4;
|
||||
dcur = 0;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 8;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
|
||||
} else {
|
||||
// Clear this register to let the digital part know 320M PLL is used
|
||||
system
|
||||
.cpu_per_conf
|
||||
.modify(|_, w| w.pll_freq_sel().clear_bit());
|
||||
|
||||
// Configure 320M PLL
|
||||
match xtal_freq {
|
||||
XtalClock::RtcXtalFreq40M => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreq32M => {
|
||||
div_ref = 1;
|
||||
div7_0 = 6;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
|
||||
XtalClock::RtcXtalFreqOther(_) => {
|
||||
div_ref = 0;
|
||||
div7_0 = 4;
|
||||
dr1 = 0;
|
||||
dr3 = 0;
|
||||
dchgp = 5;
|
||||
dcur = 3;
|
||||
dbias = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69);
|
||||
}
|
||||
|
||||
i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
i2c_bbpll_div_7_0 = div7_0;
|
||||
i2c_bbpll_dcur =
|
||||
(2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
|
||||
|
||||
regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2);
|
||||
|
||||
regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c3_rtc_bbpll_enable() {
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
|
||||
rtc_cntl.options0.modify(|_, w| {
|
||||
w.bb_i2c_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_force_pd()
|
||||
.clear_bit()
|
||||
.bbpll_i2c_force_pd()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c3_rtc_update_to_xtal(freq: XtalClock, _div: u32) {
|
||||
let system_control = unsafe { &*crate::peripherals::SYSTEM::ptr() };
|
||||
|
||||
unsafe {
|
||||
ets_update_cpu_frequency_rom(freq.mhz());
|
||||
// Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0)
|
||||
// first.
|
||||
system_control.sysclk_conf.modify(|_, w| {
|
||||
w.pre_div_cnt()
|
||||
.bits(0)
|
||||
.pre_div_cnt()
|
||||
.bits((_div - 1) as u16)
|
||||
});
|
||||
|
||||
// No need to adjust the REF_TICK
|
||||
|
||||
// Switch clock source
|
||||
system_control
|
||||
.sysclk_conf
|
||||
.modify(|_, w| w.soc_clk_sel().bits(0));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) {
|
||||
let system_control = unsafe { &*crate::peripherals::SYSTEM::ptr() };
|
||||
|
||||
unsafe {
|
||||
system_control
|
||||
.sysclk_conf
|
||||
.modify(|_, w| w.pre_div_cnt().bits(0).soc_clk_sel().bits(1));
|
||||
system_control.cpu_per_conf.modify(|_, w| {
|
||||
w.cpuperiod_sel().bits(match cpu_clock_speed {
|
||||
CpuClock::Clock80MHz => 0,
|
||||
CpuClock::Clock160MHz => 1,
|
||||
})
|
||||
});
|
||||
ets_update_cpu_frequency_rom(cpu_clock_speed.mhz());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c3_rtc_apb_freq_update(apb_freq: ApbClock) {
|
||||
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
|
||||
let value = ((apb_freq.hz() >> 12) & u16::MAX as u32)
|
||||
| (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16);
|
||||
|
||||
rtc_cntl
|
||||
.store5
|
||||
.modify(|_, w| unsafe { w.scratch5().bits(value) });
|
||||
}
|
||||
@ -1,369 +0,0 @@
|
||||
use crate::clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock};
|
||||
|
||||
extern "C" {
|
||||
fn ets_update_cpu_frequency(ticks_per_us: u32);
|
||||
}
|
||||
|
||||
const I2C_BBPLL: u8 = 0x66;
|
||||
const I2C_BBPLL_HOSTID: u8 = 0;
|
||||
|
||||
const I2C_BBPLL_OC_REF_DIV: u8 = 2;
|
||||
const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_DIV_7_0: u8 = 3;
|
||||
|
||||
const I2C_BBPLL_OC_DR1: u8 = 5;
|
||||
const I2C_BBPLL_OC_DR1_MSB: u8 = 2;
|
||||
const I2C_BBPLL_OC_DR1_LSB: u8 = 0;
|
||||
|
||||
const I2C_BBPLL_OC_DR3: u8 = 5;
|
||||
const I2C_BBPLL_OC_DR3_MSB: u8 = 6;
|
||||
const I2C_BBPLL_OC_DR3_LSB: u8 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_DCUR: u8 = 6;
|
||||
|
||||
const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;
|
||||
|
||||
const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;
|
||||
|
||||
const I2C_BBPLL_OC_VCO_DBIAS: u8 = 9;
|
||||
const I2C_BBPLL_OC_VCO_DBIAS_MSB: u8 = 1;
|
||||
const I2C_BBPLL_OC_VCO_DBIAS_LSB: u8 = 0;
|
||||
|
||||
// Analog function control register
|
||||
const I2C_MST_ANA_CONF0_REG: u32 = 0x600AF818;
|
||||
const I2C_MST_BBPLL_STOP_FORCE_HIGH: u32 = 1 << 2;
|
||||
const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 3;
|
||||
const I2C_MST_BBPLL_CAL_DONE: u32 = 1 << 24;
|
||||
|
||||
const MODEM_LPCON_CLK_CONF_FORCE_ON_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x1c;
|
||||
const MODEM_LPCON_CLK_I2C_MST_FO: u32 = 1 << 2;
|
||||
const MODEM_LPCON_I2C_MST_CLK_CONF_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x10;
|
||||
const MODEM_LPCON_CLK_I2C_MST_SEL_160M: u32 = 1 << 0;
|
||||
|
||||
pub(crate) fn esp32c6_rtc_bbpll_configure(_xtal_freq: XtalClock, _pll_freq: PllClock) {
|
||||
unsafe {
|
||||
// enable i2c mst clk by force on temporarily
|
||||
(MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).write_volatile(
|
||||
(MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).read_volatile()
|
||||
| MODEM_LPCON_CLK_I2C_MST_FO,
|
||||
);
|
||||
(MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).write_volatile(
|
||||
(MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).read_volatile()
|
||||
| MODEM_LPCON_CLK_I2C_MST_SEL_160M,
|
||||
);
|
||||
|
||||
let i2c_mst_ana_conf0_reg_ptr = I2C_MST_ANA_CONF0_REG as *mut u32;
|
||||
// BBPLL CALIBRATION START
|
||||
i2c_mst_ana_conf0_reg_ptr.write_volatile(
|
||||
i2c_mst_ana_conf0_reg_ptr.read_volatile() & !I2C_MST_BBPLL_STOP_FORCE_HIGH,
|
||||
);
|
||||
i2c_mst_ana_conf0_reg_ptr.write_volatile(
|
||||
i2c_mst_ana_conf0_reg_ptr.read_volatile() | I2C_MST_BBPLL_STOP_FORCE_LOW,
|
||||
);
|
||||
|
||||
let div_ref = 0u32;
|
||||
let div7_0 = 8u32;
|
||||
let dr1 = 0u32;
|
||||
let dr3 = 0u32;
|
||||
let dchgp = 5u32;
|
||||
let dcur = 3u32;
|
||||
let dbias = 2u32;
|
||||
|
||||
let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
|
||||
let i2c_bbpll_div_7_0 = div7_0;
|
||||
let i2c_bbpll_dcur =
|
||||
(1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
|
||||
|
||||
regi2c_write(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_REF_DIV,
|
||||
i2c_bbpll_lref as u8,
|
||||
);
|
||||
regi2c_write(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DIV_7_0,
|
||||
i2c_bbpll_div_7_0 as u8,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DR1,
|
||||
I2C_BBPLL_OC_DR1_MSB,
|
||||
I2C_BBPLL_OC_DR1_LSB,
|
||||
dr1 as u8,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DR3,
|
||||
I2C_BBPLL_OC_DR3_MSB,
|
||||
I2C_BBPLL_OC_DR3_LSB,
|
||||
dr3 as u8,
|
||||
);
|
||||
regi2c_write(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_DCUR,
|
||||
i2c_bbpll_dcur as u8,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_BBPLL,
|
||||
I2C_BBPLL_HOSTID,
|
||||
I2C_BBPLL_OC_VCO_DBIAS,
|
||||
I2C_BBPLL_OC_VCO_DBIAS_MSB,
|
||||
I2C_BBPLL_OC_VCO_DBIAS_LSB,
|
||||
dbias as u8,
|
||||
);
|
||||
|
||||
// WAIT CALIBRATION DONE
|
||||
while (i2c_mst_ana_conf0_reg_ptr.read_volatile() & I2C_MST_BBPLL_CAL_DONE) == 0 {}
|
||||
|
||||
// BBPLL CALIBRATION STOP
|
||||
i2c_mst_ana_conf0_reg_ptr.write_volatile(
|
||||
i2c_mst_ana_conf0_reg_ptr.read_volatile() | I2C_MST_BBPLL_STOP_FORCE_HIGH,
|
||||
);
|
||||
i2c_mst_ana_conf0_reg_ptr.write_volatile(
|
||||
i2c_mst_ana_conf0_reg_ptr.read_volatile() & !I2C_MST_BBPLL_STOP_FORCE_LOW,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c6_rtc_bbpll_enable() {
|
||||
let pmu = unsafe { &*crate::peripherals::PMU::PTR };
|
||||
|
||||
pmu.imm_hp_ck_power.modify(|_, w| {
|
||||
w.tie_high_xpd_bb_i2c()
|
||||
.set_bit()
|
||||
.tie_high_xpd_bbpll()
|
||||
.set_bit()
|
||||
.tie_high_xpd_bbpll_i2c()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
pmu.imm_hp_ck_power
|
||||
.modify(|_, w| w.tie_high_global_bbpll_icg().set_bit());
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c6_rtc_update_to_xtal(freq: XtalClock, _div: u8) {
|
||||
let pcr = unsafe { &*crate::peripherals::PCR::PTR };
|
||||
unsafe {
|
||||
ets_update_cpu_frequency(freq.mhz());
|
||||
// Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0)
|
||||
// first.
|
||||
pcr.apb_freq_conf
|
||||
.modify(|_, w| w.apb_div_num().bits(0).apb_div_num().bits(_div - 1));
|
||||
|
||||
// Switch clock source
|
||||
pcr.sysclk_conf.modify(|_, w| w.soc_clk_sel().bits(0));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) {
|
||||
// On ESP32C6, MSPI source clock's default HS divider leads to 120MHz, which is
|
||||
// unusable before calibration Therefore, before switching SOC_ROOT_CLK to
|
||||
// HS, we need to set MSPI source clock HS divider to make it run at
|
||||
// 80MHz after the switch. PLL = 480MHz, so divider is 6.
|
||||
clk_ll_mspi_fast_set_hs_divider(6);
|
||||
|
||||
let pcr = unsafe { &*crate::peripherals::PCR::PTR };
|
||||
unsafe {
|
||||
pcr.cpu_freq_conf.modify(|_, w| {
|
||||
w.cpu_hs_div_num()
|
||||
.bits(((480 / cpu_clock_speed.mhz() / 3) - 1) as u8)
|
||||
.cpu_hs_120m_force()
|
||||
.clear_bit()
|
||||
});
|
||||
|
||||
pcr.cpu_freq_conf
|
||||
.modify(|_, w| w.cpu_hs_120m_force().clear_bit());
|
||||
|
||||
pcr.sysclk_conf.modify(|_, w| {
|
||||
w.soc_clk_sel().bits(1) // PLL = 1
|
||||
});
|
||||
ets_update_cpu_frequency(cpu_clock_speed.mhz());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn esp32c6_rtc_apb_freq_update(apb_freq: ApbClock) {
|
||||
let lp_aon = unsafe { &*crate::peripherals::LP_AON::ptr() };
|
||||
let value = ((apb_freq.hz() >> 12) & u16::MAX as u32)
|
||||
| (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16);
|
||||
|
||||
lp_aon
|
||||
.store5
|
||||
.modify(|_, w| unsafe { w.lp_aon_store5().bits(value) });
|
||||
}
|
||||
|
||||
fn clk_ll_mspi_fast_set_hs_divider(divider: u32) {
|
||||
// SOC_ROOT_CLK ------> MSPI_FAST_CLK
|
||||
// HS divider option: 4, 5, 6 (PCR_MSPI_FAST_HS_DIV_NUM=3, 4, 5)
|
||||
let pcr = unsafe { &*crate::peripherals::PCR::PTR };
|
||||
|
||||
unsafe {
|
||||
match divider {
|
||||
4 => pcr
|
||||
.mspi_clk_conf
|
||||
.modify(|_, w| w.mspi_fast_hs_div_num().bits(3)),
|
||||
5 => pcr
|
||||
.mspi_clk_conf
|
||||
.modify(|_, w| w.mspi_fast_hs_div_num().bits(4)),
|
||||
6 => pcr
|
||||
.mspi_clk_conf
|
||||
.modify(|_, w| w.mspi_fast_hs_div_num().bits(5)),
|
||||
_ => panic!("Unsupported HS MSPI_FAST divider"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_set_bit(reg: u32, bit: u32) {
|
||||
unsafe {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | bit);
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_clr_bit(reg: u32, bit: u32) {
|
||||
unsafe {
|
||||
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !bit);
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_write(reg: u32, v: u32) {
|
||||
unsafe {
|
||||
(reg as *mut u32).write_volatile(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn reg_get_bit(reg: u32, b: u32) -> u32 {
|
||||
unsafe { (reg as *mut u32).read_volatile() & b }
|
||||
}
|
||||
|
||||
fn reg_get_field(reg: u32, s: u32, v: u32) -> u32 {
|
||||
unsafe { ((reg as *mut u32).read_volatile() >> s) & v }
|
||||
}
|
||||
|
||||
const DR_REG_MODEM_LPCON_BASE: u32 = 0x600AF000;
|
||||
const MODEM_LPCON_CLK_CONF_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x18;
|
||||
const MODEM_LPCON_CLK_I2C_MST_EN: u32 = 1 << 2;
|
||||
const DR_REG_LP_I2C_ANA_MST_BASE: u32 = 0x600B2400;
|
||||
const LP_I2C_ANA_MST_DATE_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x3fc;
|
||||
const LP_I2C_ANA_MST_I2C_MAT_CLK_EN: u32 = 1 << 28;
|
||||
const REGI2C_BIAS: u8 = 0x6a;
|
||||
const REGI2C_DIG_REG: u8 = 0x6d;
|
||||
const REGI2C_ULP_CAL: u8 = 0x61;
|
||||
const REGI2C_SAR_I2C: u8 = 0x69;
|
||||
|
||||
const LP_I2C_ANA_MST_DEVICE_EN_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x14;
|
||||
const REGI2C_BBPLL_DEVICE_EN: u32 = 1 << 5;
|
||||
const REGI2C_BIAS_DEVICE_EN: u32 = 1 << 4;
|
||||
const REGI2C_DIG_REG_DEVICE_EN: u32 = 1 << 8;
|
||||
const REGI2C_ULP_CAL_DEVICE_EN: u32 = 1 << 6;
|
||||
const REGI2C_SAR_I2C_DEVICE_EN: u32 = 1 << 7;
|
||||
|
||||
const REGI2C_RTC_SLAVE_ID_V: u8 = 0xFF;
|
||||
const REGI2C_RTC_SLAVE_ID_S: u8 = 0;
|
||||
const REGI2C_RTC_ADDR_V: u8 = 0xFF;
|
||||
const REGI2C_RTC_ADDR_S: u8 = 8;
|
||||
const REGI2C_RTC_WR_CNTL_V: u8 = 0x1;
|
||||
const REGI2C_RTC_WR_CNTL_S: u8 = 24;
|
||||
const REGI2C_RTC_DATA_V: u8 = 0xFF;
|
||||
const REGI2C_RTC_DATA_S: u8 = 16;
|
||||
|
||||
const LP_I2C_ANA_MST_I2C0_CTRL_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x0;
|
||||
const LP_I2C_ANA_MST_I2C0_BUSY: u32 = 1 << 25;
|
||||
|
||||
const LP_I2C_ANA_MST_I2C0_DATA_REG: u32 = DR_REG_LP_I2C_ANA_MST_BASE + 0x8;
|
||||
const LP_I2C_ANA_MST_I2C0_RDATA_V: u32 = 0x000000FF;
|
||||
const LP_I2C_ANA_MST_I2C0_RDATA_S: u32 = 0;
|
||||
|
||||
const REGI2C_BBPLL: u8 = 0x66;
|
||||
|
||||
fn regi2c_enable_block(block: u8) {
|
||||
reg_set_bit(MODEM_LPCON_CLK_CONF_REG, MODEM_LPCON_CLK_I2C_MST_EN);
|
||||
reg_set_bit(LP_I2C_ANA_MST_DATE_REG, LP_I2C_ANA_MST_I2C_MAT_CLK_EN);
|
||||
|
||||
// Before config I2C register, enable corresponding slave.
|
||||
match block {
|
||||
REGI2C_BBPLL => {
|
||||
reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN);
|
||||
}
|
||||
REGI2C_BIAS => {
|
||||
reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN);
|
||||
}
|
||||
REGI2C_DIG_REG => {
|
||||
reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN);
|
||||
}
|
||||
REGI2C_ULP_CAL => {
|
||||
reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN);
|
||||
}
|
||||
REGI2C_SAR_I2C => {
|
||||
reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn regi2c_disable_block(block: u8) {
|
||||
match block {
|
||||
REGI2C_BBPLL => {
|
||||
reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN);
|
||||
}
|
||||
REGI2C_BIAS => {
|
||||
reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN);
|
||||
}
|
||||
REGI2C_DIG_REG => {
|
||||
reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN);
|
||||
}
|
||||
REGI2C_ULP_CAL => {
|
||||
reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN);
|
||||
}
|
||||
REGI2C_SAR_I2C => {
|
||||
reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn regi2c_write(block: u8, _host_id: u8, reg_add: u8, data: u8) {
|
||||
regi2c_enable_block(block);
|
||||
|
||||
let temp: u32 = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32) << REGI2C_RTC_SLAVE_ID_S as u32)
|
||||
| ((reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32)
|
||||
| ((0x1 & REGI2C_RTC_WR_CNTL_V as u32) << REGI2C_RTC_WR_CNTL_S as u32) // 0: READ I2C register; 1: Write I2C register;
|
||||
| (((data as u32) & REGI2C_RTC_DATA_V as u32) << REGI2C_RTC_DATA_S as u32);
|
||||
reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp);
|
||||
while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {}
|
||||
|
||||
regi2c_disable_block(block);
|
||||
}
|
||||
|
||||
pub(crate) fn regi2c_write_mask(block: u8, _host_id: u8, reg_add: u8, msb: u8, lsb: u8, data: u8) {
|
||||
assert!(msb - lsb < 8);
|
||||
regi2c_enable_block(block);
|
||||
|
||||
// Read the i2c bus register
|
||||
let mut temp: u32 = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32)
|
||||
<< REGI2C_RTC_SLAVE_ID_S as u32)
|
||||
| (reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32;
|
||||
reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp);
|
||||
while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {}
|
||||
temp = reg_get_field(
|
||||
LP_I2C_ANA_MST_I2C0_DATA_REG,
|
||||
LP_I2C_ANA_MST_I2C0_RDATA_S,
|
||||
LP_I2C_ANA_MST_I2C0_RDATA_V,
|
||||
);
|
||||
// Write the i2c bus register
|
||||
temp &= (!(0xFFFFFFFF << lsb)) | (0xFFFFFFFF << (msb + 1));
|
||||
temp =
|
||||
((data as u32 & (!(0xFFFFFFFF << (msb as u32 - lsb as u32 + 1)))) << (lsb as u32)) | temp;
|
||||
temp = ((block as u32 & REGI2C_RTC_SLAVE_ID_V as u32) << REGI2C_RTC_SLAVE_ID_S as u32)
|
||||
| ((reg_add as u32 & REGI2C_RTC_ADDR_V as u32) << REGI2C_RTC_ADDR_S as u32)
|
||||
| ((0x1 & REGI2C_RTC_WR_CNTL_V as u32) << REGI2C_RTC_WR_CNTL_S as u32)
|
||||
| ((temp & REGI2C_RTC_DATA_V as u32) << REGI2C_RTC_DATA_S as u32);
|
||||
reg_write(LP_I2C_ANA_MST_I2C0_CTRL_REG, temp);
|
||||
while reg_get_bit(LP_I2C_ANA_MST_I2C0_CTRL_REG, LP_I2C_ANA_MST_I2C0_BUSY) != 0 {}
|
||||
|
||||
regi2c_disable_block(block);
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
use crate::clock::CpuClock;
|
||||
|
||||
pub(crate) fn set_cpu_clock(cpu_clock_speed: CpuClock) {
|
||||
let system_control = unsafe { &*crate::peripherals::SYSTEM::PTR };
|
||||
|
||||
unsafe {
|
||||
system_control
|
||||
.sysclk_conf
|
||||
.modify(|_, w| w.soc_clk_sel().bits(1));
|
||||
system_control.cpu_per_conf.modify(|_, w| {
|
||||
w.pll_freq_sel()
|
||||
.set_bit()
|
||||
.cpuperiod_sel()
|
||||
.bits(match cpu_clock_speed {
|
||||
CpuClock::Clock80MHz => 0,
|
||||
CpuClock::Clock160MHz => 1,
|
||||
CpuClock::Clock240MHz => 2,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,602 +0,0 @@
|
||||
//! # Clock Control
|
||||
use fugit::HertzU32;
|
||||
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
system::SystemClockControl,
|
||||
};
|
||||
|
||||
#[cfg_attr(esp32, path = "clocks_ll/esp32.rs")]
|
||||
#[cfg_attr(esp32c2, path = "clocks_ll/esp32c2.rs")]
|
||||
#[cfg_attr(esp32c3, path = "clocks_ll/esp32c3.rs")]
|
||||
#[cfg_attr(esp32c6, path = "clocks_ll/esp32c6.rs")]
|
||||
#[cfg_attr(esp32h2, path = "clocks_ll/esp32h2.rs")]
|
||||
#[cfg_attr(esp32s2, path = "clocks_ll/esp32s2.rs")]
|
||||
#[cfg_attr(esp32s3, path = "clocks_ll/esp32s3.rs")]
|
||||
pub(crate) mod clocks_ll;
|
||||
|
||||
pub trait Clock {
|
||||
fn frequency(&self) -> HertzU32;
|
||||
|
||||
fn mhz(&self) -> u32 {
|
||||
self.frequency().to_MHz()
|
||||
}
|
||||
|
||||
fn hz(&self) -> u32 {
|
||||
self.frequency().to_Hz()
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU clock speed
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CpuClock {
|
||||
#[cfg(not(esp32h2))]
|
||||
Clock80MHz,
|
||||
#[cfg(esp32h2)]
|
||||
Clock96MHz,
|
||||
#[cfg(esp32c2)]
|
||||
Clock120MHz,
|
||||
#[cfg(not(any(esp32c2, esp32h2)))]
|
||||
Clock160MHz,
|
||||
#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))]
|
||||
Clock240MHz,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Clock for CpuClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
#[cfg(not(esp32h2))]
|
||||
CpuClock::Clock80MHz => HertzU32::MHz(80),
|
||||
#[cfg(esp32h2)]
|
||||
CpuClock::Clock96MHz => HertzU32::MHz(96),
|
||||
#[cfg(esp32c2)]
|
||||
CpuClock::Clock120MHz => HertzU32::MHz(120),
|
||||
#[cfg(not(any(esp32c2, esp32h2)))]
|
||||
CpuClock::Clock160MHz => HertzU32::MHz(160),
|
||||
#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))]
|
||||
CpuClock::Clock240MHz => HertzU32::MHz(240),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum XtalClock {
|
||||
#[cfg(esp32)]
|
||||
RtcXtalFreq24M,
|
||||
#[cfg(any(esp32, esp32c2))]
|
||||
RtcXtalFreq26M,
|
||||
#[cfg(any(esp32c3, esp32h2, esp32s3))]
|
||||
RtcXtalFreq32M,
|
||||
#[cfg(not(esp32h2))]
|
||||
RtcXtalFreq40M,
|
||||
RtcXtalFreqOther(u32),
|
||||
}
|
||||
|
||||
impl Clock for XtalClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
#[cfg(esp32)]
|
||||
XtalClock::RtcXtalFreq24M => HertzU32::MHz(24),
|
||||
#[cfg(any(esp32, esp32c2))]
|
||||
XtalClock::RtcXtalFreq26M => HertzU32::MHz(26),
|
||||
#[cfg(any(esp32c3, esp32h2, esp32s3))]
|
||||
XtalClock::RtcXtalFreq32M => HertzU32::MHz(32),
|
||||
#[cfg(not(esp32h2))]
|
||||
XtalClock::RtcXtalFreq40M => HertzU32::MHz(40),
|
||||
XtalClock::RtcXtalFreqOther(mhz) => HertzU32::MHz(*mhz),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum PllClock {
|
||||
#[cfg(esp32h2)]
|
||||
Pll8MHz,
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
Pll48MHz,
|
||||
#[cfg(esp32h2)]
|
||||
Pll64MHz,
|
||||
#[cfg(esp32c6)]
|
||||
Pll80MHz,
|
||||
#[cfg(esp32h2)]
|
||||
Pll96MHz,
|
||||
#[cfg(esp32c6)]
|
||||
Pll120MHz,
|
||||
#[cfg(esp32c6)]
|
||||
Pll160MHz,
|
||||
#[cfg(esp32c6)]
|
||||
Pll240MHz,
|
||||
#[cfg(not(any(esp32c2, esp32c6, esp32h2)))]
|
||||
Pll320MHz,
|
||||
#[cfg(not(esp32h2))]
|
||||
Pll480MHz,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ApbClock {
|
||||
#[cfg(esp32h2)]
|
||||
ApbFreq32MHz,
|
||||
#[cfg(not(esp32h2))]
|
||||
ApbFreq40MHz,
|
||||
#[cfg(not(esp32h2))]
|
||||
ApbFreq80MHz,
|
||||
ApbFreqOther(u32),
|
||||
}
|
||||
|
||||
impl Clock for ApbClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
#[cfg(esp32h2)]
|
||||
ApbClock::ApbFreq32MHz => HertzU32::MHz(32),
|
||||
#[cfg(not(esp32h2))]
|
||||
ApbClock::ApbFreq40MHz => HertzU32::MHz(40),
|
||||
#[cfg(not(esp32h2))]
|
||||
ApbClock::ApbFreq80MHz => HertzU32::MHz(80),
|
||||
ApbClock::ApbFreqOther(mhz) => HertzU32::MHz(*mhz),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frozen clock frequencies
|
||||
///
|
||||
/// The existence of this value indicates that the clock configuration can no
|
||||
/// longer be changed
|
||||
pub struct Clocks<'d> {
|
||||
_private: PeripheralRef<'d, SystemClockControl>,
|
||||
pub cpu_clock: HertzU32,
|
||||
pub apb_clock: HertzU32,
|
||||
pub xtal_clock: HertzU32,
|
||||
#[cfg(esp32)]
|
||||
pub i2c_clock: HertzU32,
|
||||
#[cfg(esp32)]
|
||||
pub pwm_clock: HertzU32,
|
||||
#[cfg(esp32s3)]
|
||||
pub crypto_pwm_clock: HertzU32,
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pub crypto_clock: HertzU32,
|
||||
#[cfg(esp32h2)]
|
||||
pub pll_48m_clock: HertzU32,
|
||||
#[cfg(esp32h2)]
|
||||
pub pll_96m_clock: HertzU32,
|
||||
// TODO chip specific additional ones as needed
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'d> Clocks<'d> {
|
||||
/// This should not be used in user code.
|
||||
/// The whole point this exists is make it possible to have other crates
|
||||
/// (i.e. esp-wifi) create `Clocks`
|
||||
#[doc(hidden)]
|
||||
pub fn from_raw_clocks(
|
||||
system_clock_control: PeripheralRef<'d, SystemClockControl>,
|
||||
raw_clocks: RawClocks,
|
||||
) -> Clocks<'d> {
|
||||
Self {
|
||||
_private: system_clock_control,
|
||||
cpu_clock: raw_clocks.cpu_clock,
|
||||
apb_clock: raw_clocks.apb_clock,
|
||||
xtal_clock: raw_clocks.xtal_clock,
|
||||
#[cfg(esp32)]
|
||||
i2c_clock: raw_clocks.i2c_clock,
|
||||
#[cfg(esp32)]
|
||||
pwm_clock: raw_clocks.pwm_clock,
|
||||
#[cfg(esp32s3)]
|
||||
crypto_pwm_clock: raw_clocks.crypto_pwm_clock,
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
crypto_clock: raw_clocks.crypto_clock,
|
||||
#[cfg(esp32h2)]
|
||||
pll_48m_clock: raw_clocks.pll_48m_clock,
|
||||
#[cfg(esp32h2)]
|
||||
pll_96m_clock: raw_clocks.pll_96m_clock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct RawClocks {
|
||||
pub cpu_clock: HertzU32,
|
||||
pub apb_clock: HertzU32,
|
||||
pub xtal_clock: HertzU32,
|
||||
#[cfg(esp32)]
|
||||
pub i2c_clock: HertzU32,
|
||||
#[cfg(esp32)]
|
||||
pub pwm_clock: HertzU32,
|
||||
#[cfg(esp32s3)]
|
||||
pub crypto_pwm_clock: HertzU32,
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pub crypto_clock: HertzU32,
|
||||
#[cfg(esp32h2)]
|
||||
pub pll_48m_clock: HertzU32,
|
||||
#[cfg(esp32h2)]
|
||||
pub pll_96m_clock: HertzU32,
|
||||
// TODO chip specific additional ones as needed
|
||||
}
|
||||
|
||||
/// Used to configure the frequencies of the clocks present in the chip.
|
||||
///
|
||||
/// After setting all frequencies, call the freeze function to apply the
|
||||
/// configuration.
|
||||
pub struct ClockControl<'d> {
|
||||
_private: PeripheralRef<'d, SystemClockControl>,
|
||||
desired_rates: RawClocks,
|
||||
}
|
||||
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Applies the clock configuration and returns a Clocks struct that
|
||||
/// signifies that the clocks are frozen, and contains the frequencies
|
||||
/// used. After this function is called, the clocks can not change
|
||||
pub fn freeze(self) -> Clocks<'d> {
|
||||
Clocks::from_raw_clocks(self._private, self.desired_rates)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
#[cfg(feature = "esp32_40mhz")]
|
||||
return ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
i2c_clock: HertzU32::MHz(80),
|
||||
pwm_clock: HertzU32::MHz(160),
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "esp32_26mhz")]
|
||||
return ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(26),
|
||||
i2c_clock: HertzU32::MHz(80),
|
||||
pwm_clock: HertzU32::MHz(160),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
// like NuttX use 40M hardcoded - if it turns out to be a problem
|
||||
// we will take care then
|
||||
#[cfg(feature = "esp32_40mhz")]
|
||||
let xtal_freq = XtalClock::RtcXtalFreq40M;
|
||||
#[cfg(feature = "esp32_26mhz")]
|
||||
let xtal_freq = XtalClock::RtcXtalFreq26M;
|
||||
let pll_freq = match cpu_clock_speed {
|
||||
CpuClock::Clock80MHz => PllClock::Pll320MHz,
|
||||
CpuClock::Clock160MHz => PllClock::Pll320MHz,
|
||||
CpuClock::Clock240MHz => PllClock::Pll480MHz,
|
||||
};
|
||||
|
||||
clocks_ll::esp32_rtc_update_to_xtal(xtal_freq, 1);
|
||||
clocks_ll::esp32_rtc_bbpll_enable();
|
||||
clocks_ll::esp32_rtc_bbpll_configure(xtal_freq, pll_freq);
|
||||
clocks_ll::set_cpu_freq(cpu_clock_speed);
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
i2c_clock: HertzU32::MHz(40),
|
||||
// The docs are unclear here. pwm_clock seems to be tied to clocks.apb_clock
|
||||
// while simultaneously being fixed at 160 MHz.
|
||||
// Testing showed 160 MHz to be correct for current clock configurations.
|
||||
pwm_clock: HertzU32::MHz(160),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c2)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
#[cfg(feature = "esp32c2_40mhz")]
|
||||
return ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(40),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "esp32c2_26mhz")]
|
||||
return ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(40),
|
||||
xtal_clock: HertzU32::MHz(26),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
let apb_freq;
|
||||
#[cfg(feature = "esp32c2_40mhz")]
|
||||
let xtal_freq = XtalClock::RtcXtalFreq40M;
|
||||
#[cfg(feature = "esp32c2_26mhz")]
|
||||
let xtal_freq = XtalClock::RtcXtalFreq26M;
|
||||
let pll_freq = PllClock::Pll480MHz;
|
||||
|
||||
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
|
||||
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
|
||||
clocks_ll::esp32c2_rtc_update_to_xtal(xtal_freq, 1);
|
||||
clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq);
|
||||
} else {
|
||||
apb_freq = ApbClock::ApbFreq40MHz;
|
||||
clocks_ll::esp32c2_rtc_bbpll_enable();
|
||||
clocks_ll::esp32c2_rtc_bbpll_configure(xtal_freq, pll_freq);
|
||||
clocks_ll::esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed);
|
||||
clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq);
|
||||
}
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: apb_freq.frequency(),
|
||||
xtal_clock: xtal_freq.frequency(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c3)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
let apb_freq;
|
||||
let xtal_freq = XtalClock::RtcXtalFreq40M;
|
||||
let pll_freq = PllClock::Pll480MHz;
|
||||
|
||||
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
|
||||
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
|
||||
clocks_ll::esp32c3_rtc_update_to_xtal(xtal_freq, 1);
|
||||
clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq);
|
||||
} else {
|
||||
apb_freq = ApbClock::ApbFreq80MHz;
|
||||
clocks_ll::esp32c3_rtc_bbpll_enable();
|
||||
clocks_ll::esp32c3_rtc_bbpll_configure(xtal_freq, pll_freq);
|
||||
clocks_ll::esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed);
|
||||
clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq);
|
||||
}
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: apb_freq.frequency(),
|
||||
xtal_clock: xtal_freq.frequency(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
crypto_clock: HertzU32::MHz(160),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
let apb_freq;
|
||||
let xtal_freq = XtalClock::RtcXtalFreq40M;
|
||||
let pll_freq = PllClock::Pll480MHz;
|
||||
|
||||
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
|
||||
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
|
||||
clocks_ll::esp32c6_rtc_update_to_xtal(xtal_freq, 1);
|
||||
clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq);
|
||||
} else {
|
||||
apb_freq = ApbClock::ApbFreq80MHz;
|
||||
clocks_ll::esp32c6_rtc_bbpll_enable();
|
||||
clocks_ll::esp32c6_rtc_bbpll_configure(xtal_freq, pll_freq);
|
||||
clocks_ll::esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed);
|
||||
clocks_ll::esp32c6_rtc_apb_freq_update(apb_freq);
|
||||
}
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: apb_freq.frequency(),
|
||||
xtal_clock: xtal_freq.frequency(),
|
||||
crypto_clock: HertzU32::MHz(160),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32h2)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(96),
|
||||
apb_clock: HertzU32::MHz(32),
|
||||
xtal_clock: HertzU32::MHz(32),
|
||||
pll_48m_clock: HertzU32::MHz(48),
|
||||
crypto_clock: HertzU32::MHz(96),
|
||||
pll_96m_clock: HertzU32::MHz(96),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
let apb_freq;
|
||||
let xtal_freq = XtalClock::RtcXtalFreq32M;
|
||||
let pll_freq = PllClock::Pll96MHz;
|
||||
|
||||
if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
|
||||
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
|
||||
clocks_ll::esp32h2_rtc_update_to_xtal(xtal_freq, 1);
|
||||
clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq);
|
||||
} else {
|
||||
apb_freq = ApbClock::ApbFreq32MHz;
|
||||
clocks_ll::esp32h2_rtc_bbpll_enable();
|
||||
clocks_ll::esp32h2_rtc_bbpll_configure(xtal_freq, pll_freq);
|
||||
clocks_ll::esp32h2_rtc_freq_to_pll_mhz(cpu_clock_speed);
|
||||
clocks_ll::esp32h2_rtc_apb_freq_update(apb_freq);
|
||||
}
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: apb_freq.frequency(),
|
||||
xtal_clock: xtal_freq.frequency(),
|
||||
pll_48m_clock: HertzU32::MHz(48),
|
||||
crypto_clock: HertzU32::MHz(96),
|
||||
pll_96m_clock: HertzU32::MHz(96),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
clocks_ll::set_cpu_clock(cpu_clock_speed);
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
impl<'d> ClockControl<'d> {
|
||||
/// Use what is considered the default settings after boot.
|
||||
#[allow(unused)]
|
||||
pub fn boot_defaults(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
) -> ClockControl<'d> {
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: HertzU32::MHz(80),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
crypto_pwm_clock: HertzU32::MHz(160),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the CPU clock speed.
|
||||
#[allow(unused)]
|
||||
pub fn configure(
|
||||
clock_control: impl Peripheral<P = SystemClockControl> + 'd,
|
||||
cpu_clock_speed: CpuClock,
|
||||
) -> ClockControl<'d> {
|
||||
clocks_ll::set_cpu_clock(cpu_clock_speed);
|
||||
|
||||
ClockControl {
|
||||
_private: clock_control.into_ref(),
|
||||
desired_rates: RawClocks {
|
||||
cpu_clock: cpu_clock_speed.frequency(),
|
||||
apb_clock: HertzU32::MHz(80),
|
||||
xtal_clock: HertzU32::MHz(40),
|
||||
crypto_pwm_clock: HertzU32::MHz(160),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
//! Delay driver implement the blocking [DelayMs] and [DelayUs] traits from
|
||||
//! [embedded-hal].
|
||||
//!
|
||||
//! The delays are implemented in a "best-effort" way, meaning that the CPU will
|
||||
//! block for at least the amount of time specified, but accuracy can be
|
||||
//! affected by many factors, including interrupt usage.
|
||||
//!
|
||||
//! [DelayMs]: embedded_hal::blocking::delay::DelayMs
|
||||
//! [DelayUs]: embedded_hal::blocking::delay::DelayUs
|
||||
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
|
||||
|
||||
use fugit::HertzU64;
|
||||
|
||||
/// Delay driver
|
||||
///
|
||||
/// Uses the `SYSTIMER` peripheral internally for RISC-V devices, and the
|
||||
/// built-in Xtensa timer for Xtensa devices.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Delay {
|
||||
freq: HertzU64,
|
||||
}
|
||||
|
||||
impl<T> embedded_hal::blocking::delay::DelayMs<T> for Delay
|
||||
where
|
||||
T: Into<u32>,
|
||||
{
|
||||
fn delay_ms(&mut self, ms: T) {
|
||||
for _ in 0..ms.into() {
|
||||
self.delay(1000u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> embedded_hal::blocking::delay::DelayUs<T> for Delay
|
||||
where
|
||||
T: Into<u32>,
|
||||
{
|
||||
fn delay_us(&mut self, us: T) {
|
||||
self.delay(us.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1")]
|
||||
impl embedded_hal_1::delay::DelayUs for Delay {
|
||||
fn delay_us(&mut self, us: u32) {
|
||||
self.delay(us);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(riscv)]
|
||||
mod delay {
|
||||
use super::*;
|
||||
use crate::{clock::Clocks, systimer::SystemTimer};
|
||||
|
||||
impl Delay {
|
||||
/// Create a new `Delay` instance
|
||||
pub fn new(clocks: &Clocks) -> Self {
|
||||
// The counters and comparators are driven using `XTAL_CLK`.
|
||||
// The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
|
||||
// The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
|
||||
Self {
|
||||
freq: HertzU64::MHz(clocks.xtal_clock.to_MHz() as u64 * 10 / 25),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delay for the specified number of microseconds
|
||||
pub fn delay(&self, us: u32) {
|
||||
let t0 = SystemTimer::now();
|
||||
let clocks = us as u64 * (self.freq / HertzU64::MHz(1));
|
||||
|
||||
while SystemTimer::now().wrapping_sub(t0) & SystemTimer::BIT_MASK <= clocks {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(xtensa)]
|
||||
mod delay {
|
||||
use super::*;
|
||||
use crate::clock::Clocks;
|
||||
|
||||
impl Delay {
|
||||
/// Create a new `Delay` instance
|
||||
pub fn new(clocks: &Clocks) -> Self {
|
||||
Self {
|
||||
freq: clocks.cpu_clock.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delay for the specified number of microseconds
|
||||
pub fn delay(&self, us: u32) {
|
||||
let clocks = us as u64 * (self.freq / HertzU64::MHz(1));
|
||||
xtensa_lx::timer::delay(clocks as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,506 +0,0 @@
|
||||
//! Direct Memory Access
|
||||
|
||||
use crate::{
|
||||
dma::*,
|
||||
peripheral::PeripheralRef,
|
||||
system::{Peripheral, PeripheralClockControl},
|
||||
};
|
||||
|
||||
macro_rules! impl_channel {
|
||||
($num: literal) => {
|
||||
paste::paste! {
|
||||
pub struct [<Channel $num>] {}
|
||||
|
||||
impl RegisterAccess for [<Channel $num>] {
|
||||
fn init_channel() {
|
||||
// nothing special to be done here
|
||||
}
|
||||
|
||||
fn set_out_burstmode(burst_mode: bool) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_conf0_ch $num>].modify(|_,w| {
|
||||
w.out_data_burst_en().bit(burst_mode)
|
||||
.outdscr_burst_en().bit(burst_mode)
|
||||
});
|
||||
}
|
||||
|
||||
fn set_out_priority(priority: DmaPriority) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_pri_ch $num>].write(|w| {
|
||||
w.tx_pri().variant(priority as u8)
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_out_interrupts() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
dma.[<int_clr_ch $num>].write(|w| {
|
||||
w.out_eof()
|
||||
.set_bit()
|
||||
.out_dscr_err()
|
||||
.set_bit()
|
||||
.out_done()
|
||||
.set_bit()
|
||||
.out_total_eof()
|
||||
.set_bit()
|
||||
.outfifo_ovf()
|
||||
.set_bit()
|
||||
.outfifo_udf()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
dma.[<out_int_clr_ch $num>].write(|w| {
|
||||
w.out_eof()
|
||||
.set_bit()
|
||||
.out_dscr_err()
|
||||
.set_bit()
|
||||
.out_done()
|
||||
.set_bit()
|
||||
.out_total_eof()
|
||||
.set_bit()
|
||||
.outfifo_ovf()
|
||||
.set_bit()
|
||||
.outfifo_udf()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
dma.[<out_int_clr_ch $num>].write(|w| {
|
||||
w.out_eof()
|
||||
.set_bit()
|
||||
.out_dscr_err()
|
||||
.set_bit()
|
||||
.out_done()
|
||||
.set_bit()
|
||||
.out_total_eof()
|
||||
.set_bit()
|
||||
.outfifo_ovf_l1()
|
||||
.set_bit()
|
||||
.outfifo_ovf_l3()
|
||||
.set_bit()
|
||||
.outfifo_udf_l1()
|
||||
.set_bit()
|
||||
.outfifo_udf_l3()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_out() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_conf0_ch $num>].modify(|_, w| w.out_rst().set_bit());
|
||||
dma.[<out_conf0_ch $num>].modify(|_, w| w.out_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_out_descriptors(address: u32) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_link_ch $num>].modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_out_descriptor_error() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
let ret = dma.[<int_raw_ch $num>].read().out_dscr_err().bit();
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
let ret = dma.[<out_int_raw_ch $num>].read().out_dscr_err().bit();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn set_out_peripheral(peripheral: u8) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_peri_sel_ch $num>].modify(|_, w| w.peri_out_sel().variant(peripheral));
|
||||
}
|
||||
|
||||
fn start_out() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<out_link_ch $num>].modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_out_done() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
let ret = dma.[<int_raw_ch $num>].read().out_total_eof().bit();
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
let ret = dma.[<out_int_raw_ch $num>].read().out_total_eof().bit();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn last_out_dscr_address() -> usize {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
dma.[<out_eof_des_addr_ch $num>].read().out_eof_des_addr().bits() as usize
|
||||
}
|
||||
|
||||
fn is_out_eof_interrupt_set() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
let ret = dma.[<int_raw_ch $num>].read().out_eof().bit();
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
let ret = dma.[<out_int_raw_ch $num>].read().out_eof().bit();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn reset_out_eof_interrupt() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
dma.[<int_clr_ch $num>].write(|w| {
|
||||
w.out_eof()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
dma.[<out_int_clr_ch $num>].write(|w| {
|
||||
w.out_eof()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_in_burstmode(burst_mode: bool) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_conf0_ch $num>].modify(|_,w| {
|
||||
w.in_data_burst_en().bit(burst_mode).indscr_burst_en().bit(burst_mode)
|
||||
});
|
||||
}
|
||||
|
||||
fn set_in_priority(priority: DmaPriority) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_pri_ch $num>].write(|w| {
|
||||
w.rx_pri().variant(priority as u8)
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_in_interrupts() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
dma.[<int_clr_ch $num>].write(|w| {
|
||||
w.in_suc_eof()
|
||||
.set_bit()
|
||||
.in_err_eof()
|
||||
.set_bit()
|
||||
.in_dscr_err()
|
||||
.set_bit()
|
||||
.in_dscr_empty()
|
||||
.set_bit()
|
||||
.in_done()
|
||||
.set_bit()
|
||||
.infifo_ovf()
|
||||
.set_bit()
|
||||
.infifo_udf()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
dma.[<in_int_clr_ch $num>].write(|w| {
|
||||
w.in_suc_eof()
|
||||
.set_bit()
|
||||
.in_err_eof()
|
||||
.set_bit()
|
||||
.in_dscr_err()
|
||||
.set_bit()
|
||||
.in_dscr_empty()
|
||||
.set_bit()
|
||||
.in_done()
|
||||
.set_bit()
|
||||
.infifo_ovf()
|
||||
.set_bit()
|
||||
.infifo_udf()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
dma.[<in_int_clr_ch $num>].write(|w| {
|
||||
w.in_suc_eof()
|
||||
.set_bit()
|
||||
.in_err_eof()
|
||||
.set_bit()
|
||||
.in_dscr_err()
|
||||
.set_bit()
|
||||
.in_dscr_empty()
|
||||
.set_bit()
|
||||
.in_done()
|
||||
.set_bit()
|
||||
.infifo_ovf_l1()
|
||||
.set_bit()
|
||||
.infifo_ovf_l3()
|
||||
.set_bit()
|
||||
.infifo_udf_l1()
|
||||
.set_bit()
|
||||
.infifo_udf_l3()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_in() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_conf0_ch $num>].modify(|_, w| w.in_rst().set_bit());
|
||||
dma.[<in_conf0_ch $num>].modify(|_, w| w.in_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_in_descriptors(address: u32) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_link_ch $num>].modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_in_descriptor_error() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
let ret = dma.[<int_raw_ch $num>].read().in_dscr_err().bit();
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
let ret = dma.[<in_int_raw_ch $num>].read().in_dscr_err().bit();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn set_in_peripheral(peripheral: u8) {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_peri_sel_ch $num>].modify(|_, w| w.peri_in_sel().variant(peripheral));
|
||||
}
|
||||
|
||||
fn start_in() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
dma.[<in_link_ch $num>].modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_in_done() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2, esp32s3)))]
|
||||
let ret = dma.[<int_raw_ch $num>].read().in_suc_eof().bit();
|
||||
#[cfg(any(esp32c6, esp32h2, esp32s3))]
|
||||
let ret = dma.[<in_int_raw_ch $num>].read().in_suc_eof().bit();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn last_in_dscr_address() -> usize {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
dma.[<in_dscr_bf0_ch $num>].read().inlink_dscr_bf0().bits() as usize
|
||||
}
|
||||
|
||||
fn is_listening_in_eof() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<in_int_ena_ch $num>].read().in_suc_eof().bit_is_set()
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].read().in_suc_eof().bit_is_set()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_listening_out_eof() -> bool {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<out_int_ena_ch $num>].read().out_total_eof().bit_is_set()
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].read().out_total_eof().bit_is_set()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listen_in_eof() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<in_int_ena_ch $num>].modify(|_, w| w.in_suc_eof().set_bit())
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].modify(|_, w| w.in_suc_eof().set_bit())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listen_out_eof() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<out_int_ena_ch $num>].modify(|_, w| w.out_total_eof().set_bit())
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].modify(|_, w| w.out_total_eof().set_bit())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unlisten_in_eof() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<in_int_ena_ch $num>].modify(|_, w| w.in_suc_eof().clear_bit())
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].modify(|_, w| w.in_suc_eof().clear_bit())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unlisten_out_eof() {
|
||||
let dma = unsafe { &*crate::peripherals::DMA::PTR };
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32c6, esp32h2, esp32s3))] {
|
||||
dma.[<out_int_ena_ch $num>].modify(|_, w| w.out_total_eof().clear_bit())
|
||||
} else {
|
||||
dma.[<int_ena_ch $num>].modify(|_, w| w.out_total_eof().clear_bit())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Channel $num TxImpl>] {}
|
||||
|
||||
impl<'a> TxChannel<[<Channel $num>]> for [<Channel $num TxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Channel $num RxImpl>] {}
|
||||
|
||||
impl<'a> RxChannel<[<Channel $num>]> for [<Channel $num RxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<ChannelCreator $num>] {}
|
||||
|
||||
impl [<ChannelCreator $num>] {
|
||||
/// Configure the channel for use
|
||||
///
|
||||
/// Descriptors should be sized as (BUFFERSIZE / 4092) * 3
|
||||
pub fn configure<'a>(
|
||||
self,
|
||||
burst_mode: bool,
|
||||
tx_descriptors: &'a mut [u32],
|
||||
rx_descriptors: &'a mut [u32],
|
||||
priority: DmaPriority,
|
||||
) -> Channel<ChannelTx<'a, [<Channel $num TxImpl>], [<Channel $num>]>, ChannelRx<'a, [<Channel $num RxImpl>], [<Channel $num>]>, [<SuitablePeripheral $num>]> {
|
||||
let mut tx_impl = [<Channel $num TxImpl>] {};
|
||||
tx_impl.init(burst_mode, priority);
|
||||
|
||||
let tx_channel = ChannelTx {
|
||||
descriptors: tx_descriptors,
|
||||
burst_mode,
|
||||
tx_impl: tx_impl,
|
||||
write_offset: 0,
|
||||
write_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
buffer_start: core::ptr::null(),
|
||||
buffer_len: 0,
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
let mut rx_impl = [<Channel $num RxImpl>] {};
|
||||
rx_impl.init(burst_mode, priority);
|
||||
|
||||
let rx_channel = ChannelRx {
|
||||
descriptors: rx_descriptors,
|
||||
burst_mode,
|
||||
rx_impl: rx_impl,
|
||||
read_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
read_buffer_start: core::ptr::null(),
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
Channel {
|
||||
tx: tx_channel,
|
||||
rx: rx_channel,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<SuitablePeripheral $num>] {}
|
||||
impl PeripheralMarker for [<SuitablePeripheral $num>] {}
|
||||
|
||||
// with GDMA every channel can be used for any peripheral
|
||||
impl SpiPeripheral for [<SuitablePeripheral $num>] {}
|
||||
impl Spi2Peripheral for [<SuitablePeripheral $num>] {}
|
||||
#[cfg(esp32s3)]
|
||||
impl Spi3Peripheral for [<SuitablePeripheral $num>] {}
|
||||
impl I2sPeripheral for [<SuitablePeripheral $num>] {}
|
||||
impl I2s0Peripheral for [<SuitablePeripheral $num>] {}
|
||||
impl I2s1Peripheral for [<SuitablePeripheral $num>] {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_channel!(0);
|
||||
#[cfg(not(esp32c2))]
|
||||
impl_channel!(1);
|
||||
#[cfg(not(esp32c2))]
|
||||
impl_channel!(2);
|
||||
#[cfg(esp32s3)]
|
||||
impl_channel!(3);
|
||||
#[cfg(esp32s3)]
|
||||
impl_channel!(4);
|
||||
|
||||
/// GDMA Peripheral
|
||||
///
|
||||
/// This offers the available DMA channels.
|
||||
pub struct Gdma<'d> {
|
||||
_inner: PeripheralRef<'d, crate::peripherals::DMA>,
|
||||
pub channel0: ChannelCreator0,
|
||||
#[cfg(not(esp32c2))]
|
||||
pub channel1: ChannelCreator1,
|
||||
#[cfg(not(esp32c2))]
|
||||
pub channel2: ChannelCreator2,
|
||||
#[cfg(esp32s3)]
|
||||
pub channel3: ChannelCreator3,
|
||||
#[cfg(esp32s3)]
|
||||
pub channel4: ChannelCreator4,
|
||||
}
|
||||
|
||||
impl<'d> Gdma<'d> {
|
||||
/// Create a DMA instance.
|
||||
pub fn new(
|
||||
dma: impl crate::peripheral::Peripheral<P = crate::peripherals::DMA> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Gdma<'d> {
|
||||
crate::into_ref!(dma);
|
||||
|
||||
peripheral_clock_control.enable(Peripheral::Gdma);
|
||||
dma.misc_conf.modify(|_, w| w.ahbm_rst_inter().set_bit());
|
||||
dma.misc_conf.modify(|_, w| w.ahbm_rst_inter().clear_bit());
|
||||
dma.misc_conf.modify(|_, w| w.clk_en().set_bit());
|
||||
|
||||
Gdma {
|
||||
_inner: dma,
|
||||
channel0: ChannelCreator0 {},
|
||||
#[cfg(not(esp32c2))]
|
||||
channel1: ChannelCreator1 {},
|
||||
#[cfg(not(esp32c2))]
|
||||
channel2: ChannelCreator2 {},
|
||||
#[cfg(esp32s3)]
|
||||
channel3: ChannelCreator3 {},
|
||||
#[cfg(esp32s3)]
|
||||
channel4: ChannelCreator4 {},
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,571 +0,0 @@
|
||||
//! Direct Memory Access
|
||||
|
||||
use crate::{
|
||||
dma::*,
|
||||
peripheral::PeripheralRef,
|
||||
system::{Peripheral, PeripheralClockControl},
|
||||
};
|
||||
|
||||
macro_rules! ImplSpiChannel {
|
||||
($num: literal) => {
|
||||
paste::paste! {
|
||||
pub struct [<Spi $num DmaChannel>] {}
|
||||
|
||||
impl RegisterAccess for [<Spi $num DmaChannel>] {
|
||||
fn init_channel() {
|
||||
// (only) on ESP32 we need to configure DPORT for the SPI DMA channels
|
||||
#[cfg(esp32)]
|
||||
{
|
||||
let dport = unsafe { &*crate::peripherals::DPORT::PTR };
|
||||
|
||||
match $num {
|
||||
2 => {
|
||||
dport
|
||||
.spi_dma_chan_sel
|
||||
.modify(|_, w| w.spi2_dma_chan_sel().variant(1));
|
||||
},
|
||||
3 => {
|
||||
dport
|
||||
.spi_dma_chan_sel
|
||||
.modify(|_, w| w.spi3_dma_chan_sel().variant(2));
|
||||
},
|
||||
_ => panic!("Only SPI2 and SPI3 supported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_out_burstmode(burst_mode: bool) {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_conf
|
||||
.modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_out_priority(_priority: DmaPriority) {}
|
||||
|
||||
fn clear_out_interrupts() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_clr.write(|w| {
|
||||
w.out_done_int_clr()
|
||||
.set_bit()
|
||||
.out_eof_int_clr()
|
||||
.set_bit()
|
||||
.out_total_eof_int_clr()
|
||||
.set_bit()
|
||||
.outlink_dscr_error_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_out() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_conf.modify(|_, w| w.out_rst().set_bit());
|
||||
spi.dma_conf.modify(|_, w| w.out_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_out_descriptors(address: u32) {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_out_link
|
||||
.modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_out_descriptor_error() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_raw.read().outlink_dscr_error_int_raw().bit()
|
||||
}
|
||||
|
||||
fn set_out_peripheral(_peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start_out() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_out_link.modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_out_done() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
// FIXME this should be out_total_eof_int_raw? but on esp32 this interrupt doesn't seem to fire
|
||||
spi.dma_int_raw.read().out_eof_int_raw().bit()
|
||||
}
|
||||
|
||||
fn last_out_dscr_address() -> usize {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.out_eof_des_addr.read().dma_out_eof_des_addr().bits() as usize
|
||||
}
|
||||
|
||||
fn is_out_eof_interrupt_set() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_raw.read().out_eof_int_raw().bit()
|
||||
}
|
||||
|
||||
fn reset_out_eof_interrupt() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_clr.write(|w| {
|
||||
w.out_eof_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_in_burstmode(burst_mode: bool) {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_conf
|
||||
.modify(|_, w| w.indscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_in_priority(_priority: DmaPriority) {}
|
||||
|
||||
fn clear_in_interrupts() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_clr.write(|w| {
|
||||
w.in_done_int_clr()
|
||||
.set_bit()
|
||||
.in_err_eof_int_clr()
|
||||
.set_bit()
|
||||
.in_suc_eof_int_clr()
|
||||
.set_bit()
|
||||
.inlink_dscr_error_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_in() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_conf.modify(|_, w| w.in_rst().set_bit());
|
||||
spi.dma_conf.modify(|_, w| w.in_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_in_descriptors(address: u32) {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_in_link
|
||||
.modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_in_descriptor_error() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_raw.read().inlink_dscr_error_int_raw().bit()
|
||||
}
|
||||
|
||||
fn set_in_peripheral(_peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start_in() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_in_link.modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_in_done() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_raw.read().in_done_int_raw().bit()
|
||||
}
|
||||
|
||||
fn last_in_dscr_address() -> usize {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.inlink_dscr_bf0.read().dma_inlink_dscr_bf0().bits() as usize
|
||||
}
|
||||
|
||||
fn is_listening_in_eof() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.read().in_suc_eof_int_ena().bit_is_set()
|
||||
}
|
||||
|
||||
fn is_listening_out_eof() -> bool {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.read().out_total_eof_int_ena().bit_is_set()
|
||||
}
|
||||
|
||||
fn listen_in_eof() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.modify(|_, w| w.in_suc_eof_int_ena().set_bit());
|
||||
}
|
||||
|
||||
fn listen_out_eof() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.modify(|_, w| w.out_total_eof_int_ena().set_bit());
|
||||
}
|
||||
|
||||
fn unlisten_in_eof() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.modify(|_, w| w.in_suc_eof_int_ena().clear_bit());
|
||||
}
|
||||
|
||||
fn unlisten_out_eof() {
|
||||
let spi = unsafe { &*crate::peripherals::[<SPI $num>]::PTR };
|
||||
spi.dma_int_ena.modify(|_, w| w.out_total_eof_int_ena().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Spi $num DmaChannelTxImpl>] {}
|
||||
|
||||
impl<'a> TxChannel<[<Spi $num DmaChannel>]> for [<Spi $num DmaChannelTxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Spi $num DmaChannelRxImpl>] {}
|
||||
|
||||
impl<'a> RxChannel<[<Spi $num DmaChannel>]> for [<Spi $num DmaChannelRxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<Spi $num DmaChannelCreator>] {}
|
||||
|
||||
impl [<Spi $num DmaChannelCreator>] {
|
||||
/// Configure the channel for use
|
||||
///
|
||||
/// Descriptors should be sized as (BUFFERSIZE / 4092) * 3
|
||||
pub fn configure<'a>(
|
||||
self,
|
||||
burst_mode: bool,
|
||||
tx_descriptors: &'a mut [u32],
|
||||
rx_descriptors: &'a mut [u32],
|
||||
priority: DmaPriority,
|
||||
) -> Channel<
|
||||
ChannelTx<'a,[<Spi $num DmaChannelTxImpl>], [<Spi $num DmaChannel>]>,
|
||||
ChannelRx<'a,[<Spi $num DmaChannelRxImpl>], [<Spi $num DmaChannel>]>,
|
||||
[<Spi $num DmaSuitablePeripheral>],
|
||||
> {
|
||||
let mut tx_impl = [<Spi $num DmaChannelTxImpl>] {};
|
||||
tx_impl.init(burst_mode, priority);
|
||||
|
||||
let tx_channel = ChannelTx {
|
||||
descriptors: tx_descriptors,
|
||||
burst_mode,
|
||||
tx_impl: tx_impl,
|
||||
write_offset: 0,
|
||||
write_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
buffer_start: core::ptr::null(),
|
||||
buffer_len: 0,
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
let mut rx_impl = [<Spi $num DmaChannelRxImpl>] {};
|
||||
rx_impl.init(burst_mode, priority);
|
||||
|
||||
let rx_channel = ChannelRx {
|
||||
descriptors: rx_descriptors,
|
||||
burst_mode,
|
||||
rx_impl: rx_impl,
|
||||
read_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
read_buffer_start: core::ptr::null(),
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
Channel {
|
||||
tx: tx_channel,
|
||||
rx: rx_channel,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ImplI2sChannel {
|
||||
($num: literal, $peripheral: literal) => {
|
||||
paste::paste! {
|
||||
pub struct [<I2s $num DmaChannel>] {}
|
||||
|
||||
impl RegisterAccess for [<I2s $num DmaChannel>] {
|
||||
fn init_channel() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
fn set_out_burstmode(burst_mode: bool) {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.lc_conf
|
||||
.modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_out_priority(_priority: DmaPriority) {}
|
||||
|
||||
fn clear_out_interrupts() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_clr.write(|w| {
|
||||
w.out_done_int_clr()
|
||||
.set_bit()
|
||||
.out_eof_int_clr()
|
||||
.set_bit()
|
||||
.out_total_eof_int_clr()
|
||||
.set_bit()
|
||||
.out_dscr_err_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_out() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.lc_conf.modify(|_, w| w.out_rst().set_bit());
|
||||
reg_block.lc_conf.modify(|_, w| w.out_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_out_descriptors(address: u32) {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.out_link
|
||||
.modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_out_descriptor_error() -> bool {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_raw.read().out_dscr_err_int_raw().bit()
|
||||
}
|
||||
|
||||
fn set_out_peripheral(_peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start_out() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.out_link.modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_out_done() -> bool {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_raw.read().out_done_int_raw().bit()
|
||||
}
|
||||
|
||||
fn last_out_dscr_address() -> usize {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.out_eof_des_addr.read().out_eof_des_addr().bits() as usize
|
||||
}
|
||||
|
||||
fn is_out_eof_interrupt_set() -> bool {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_raw.read().out_eof_int_raw().bit()
|
||||
}
|
||||
|
||||
fn reset_out_eof_interrupt() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_clr.write(|w| {
|
||||
w.out_eof_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_in_burstmode(burst_mode: bool) {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.lc_conf
|
||||
.modify(|_, w| w.indscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_in_priority(_priority: DmaPriority) {}
|
||||
|
||||
fn clear_in_interrupts() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_clr.write(|w| {
|
||||
w.in_done_int_clr()
|
||||
.set_bit()
|
||||
.in_err_eof_int_clr()
|
||||
.set_bit()
|
||||
.in_suc_eof_int_clr()
|
||||
.set_bit()
|
||||
.in_dscr_err_int_clr()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn reset_in() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.lc_conf.modify(|_, w| w.in_rst().set_bit());
|
||||
reg_block.lc_conf.modify(|_, w| w.in_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_in_descriptors(address: u32) {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.in_link
|
||||
.modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn has_in_descriptor_error() -> bool {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_raw.read().in_dscr_err_int_raw().bit()
|
||||
}
|
||||
|
||||
fn set_in_peripheral(_peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start_in() {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.in_link.modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn is_in_done() -> bool {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.int_raw.read().in_done_int_raw().bit()
|
||||
}
|
||||
|
||||
fn last_in_dscr_address() -> usize {
|
||||
let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR };
|
||||
reg_block.inlink_dscr_bf0.read().inlink_dscr_bf0().bits() as usize
|
||||
}
|
||||
|
||||
fn is_listening_in_eof() -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn is_listening_out_eof() -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn listen_in_eof() {
|
||||
todo!()
|
||||
}
|
||||
fn listen_out_eof() {
|
||||
todo!()
|
||||
}
|
||||
fn unlisten_in_eof() {
|
||||
todo!()
|
||||
}
|
||||
fn unlisten_out_eof() {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<I2s $num DmaChannelTxImpl>] {}
|
||||
|
||||
impl<'a> TxChannel<[<I2s $num DmaChannel>]> for [<I2s $num DmaChannelTxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<I2s $num DmaChannelRxImpl>] {}
|
||||
|
||||
impl<'a> RxChannel<[<I2s $num DmaChannel>]> for [<I2s $num DmaChannelRxImpl>] {
|
||||
#[cfg(feature = "async")]
|
||||
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker {
|
||||
static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
|
||||
pub struct [<I2s $num DmaChannelCreator>] {}
|
||||
|
||||
impl [<I2s $num DmaChannelCreator>] {
|
||||
/// Configure the channel for use
|
||||
///
|
||||
/// Descriptors should be sized as (BUFFERSIZE / 4092) * 3
|
||||
pub fn configure<'a>(
|
||||
self,
|
||||
burst_mode: bool,
|
||||
tx_descriptors: &'a mut [u32],
|
||||
rx_descriptors: &'a mut [u32],
|
||||
priority: DmaPriority,
|
||||
) -> Channel<
|
||||
ChannelTx<'a,[<I2s $num DmaChannelTxImpl>], [<I2s $num DmaChannel>]>,
|
||||
ChannelRx<'a,[<I2s $num DmaChannelRxImpl>], [<I2s $num DmaChannel>]>,
|
||||
[<I2s $num DmaSuitablePeripheral>],
|
||||
> {
|
||||
let mut tx_impl = [<I2s $num DmaChannelTxImpl>] {};
|
||||
tx_impl.init(burst_mode, priority);
|
||||
|
||||
let tx_channel = ChannelTx {
|
||||
descriptors: tx_descriptors,
|
||||
burst_mode,
|
||||
tx_impl: tx_impl,
|
||||
write_offset: 0,
|
||||
write_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
buffer_start: core::ptr::null(),
|
||||
buffer_len: 0,
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
let mut rx_impl = [<I2s $num DmaChannelRxImpl>] {};
|
||||
rx_impl.init(burst_mode, priority);
|
||||
|
||||
let rx_channel = ChannelRx {
|
||||
descriptors: rx_descriptors,
|
||||
burst_mode,
|
||||
rx_impl: rx_impl,
|
||||
read_descr_ptr: core::ptr::null(),
|
||||
available: 0,
|
||||
last_seen_handled_descriptor_ptr: core::ptr::null(),
|
||||
read_buffer_start: core::ptr::null(),
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
Channel {
|
||||
tx: tx_channel,
|
||||
rx: rx_channel,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Spi2DmaSuitablePeripheral {}
|
||||
impl PeripheralMarker for Spi2DmaSuitablePeripheral {}
|
||||
impl SpiPeripheral for Spi2DmaSuitablePeripheral {}
|
||||
impl Spi2Peripheral for Spi2DmaSuitablePeripheral {}
|
||||
|
||||
pub struct Spi3DmaSuitablePeripheral {}
|
||||
impl PeripheralMarker for Spi3DmaSuitablePeripheral {}
|
||||
impl SpiPeripheral for Spi3DmaSuitablePeripheral {}
|
||||
impl Spi3Peripheral for Spi3DmaSuitablePeripheral {}
|
||||
|
||||
ImplSpiChannel!(2);
|
||||
ImplSpiChannel!(3);
|
||||
|
||||
pub struct I2s0DmaSuitablePeripheral {}
|
||||
impl PeripheralMarker for I2s0DmaSuitablePeripheral {}
|
||||
impl I2sPeripheral for I2s0DmaSuitablePeripheral {}
|
||||
impl I2s0Peripheral for I2s0DmaSuitablePeripheral {}
|
||||
|
||||
ImplI2sChannel!(0, "I2S0");
|
||||
|
||||
pub struct I2s1DmaSuitablePeripheral {}
|
||||
impl PeripheralMarker for I2s1DmaSuitablePeripheral {}
|
||||
impl I2sPeripheral for I2s1DmaSuitablePeripheral {}
|
||||
impl I2s1Peripheral for I2s1DmaSuitablePeripheral {}
|
||||
|
||||
#[cfg(esp32)]
|
||||
ImplI2sChannel!(1, "I2S1");
|
||||
|
||||
/// DMA Peripheral
|
||||
///
|
||||
/// This offers the available DMA channels.
|
||||
pub struct Dma<'d> {
|
||||
_inner: PeripheralRef<'d, crate::system::Dma>,
|
||||
pub spi2channel: Spi2DmaChannelCreator,
|
||||
pub spi3channel: Spi3DmaChannelCreator,
|
||||
pub i2s0channel: I2s0DmaChannelCreator,
|
||||
#[cfg(esp32)]
|
||||
pub i2s1channel: I2s1DmaChannelCreator,
|
||||
}
|
||||
|
||||
impl<'d> Dma<'d> {
|
||||
/// Create a DMA instance.
|
||||
pub fn new(
|
||||
dma: impl crate::peripheral::Peripheral<P = crate::system::Dma> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Dma<'d> {
|
||||
peripheral_clock_control.enable(Peripheral::Dma);
|
||||
|
||||
Dma {
|
||||
_inner: dma.into_ref(),
|
||||
spi2channel: Spi2DmaChannelCreator {},
|
||||
spi3channel: Spi3DmaChannelCreator {},
|
||||
i2s0channel: I2s0DmaChannelCreator {},
|
||||
#[cfg(esp32)]
|
||||
i2s1channel: I2s1DmaChannelCreator {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
use core::{cell::Cell, ptr};
|
||||
|
||||
use embassy_time::driver::{AlarmHandle, Driver};
|
||||
|
||||
#[cfg_attr(
|
||||
all(systimer, feature = "embassy-time-systick",),
|
||||
path = "time_driver_systimer.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(timg0, feature = "embassy-time-timg0"),
|
||||
path = "time_driver_timg.rs"
|
||||
)]
|
||||
mod time_driver;
|
||||
|
||||
use time_driver::EmbassyTimer;
|
||||
|
||||
use crate::clock::Clocks;
|
||||
|
||||
pub fn init(clocks: &Clocks, td: time_driver::TimerType) {
|
||||
EmbassyTimer::init(clocks, td)
|
||||
}
|
||||
|
||||
pub struct AlarmState {
|
||||
pub timestamp: Cell<u64>,
|
||||
|
||||
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||
// but fn pointers aren't allowed in const yet
|
||||
pub callback: Cell<*const ()>,
|
||||
pub ctx: Cell<*mut ()>,
|
||||
pub allocated: Cell<bool>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AlarmState {}
|
||||
|
||||
impl AlarmState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
timestamp: Cell::new(u64::MAX),
|
||||
callback: Cell::new(ptr::null()),
|
||||
ctx: Cell::new(ptr::null_mut()),
|
||||
allocated: Cell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for EmbassyTimer {
|
||||
fn now(&self) -> u64 {
|
||||
EmbassyTimer::now()
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
return critical_section::with(|cs| {
|
||||
let alarms = self.alarms.borrow(cs);
|
||||
for i in 0..time_driver::ALARM_COUNT {
|
||||
let c = alarms.get_unchecked(i);
|
||||
if !c.allocated.get() {
|
||||
// set alarm so it is not overwritten
|
||||
c.allocated.set(true);
|
||||
return Option::Some(AlarmHandle::new(i as u8));
|
||||
}
|
||||
}
|
||||
return Option::None;
|
||||
});
|
||||
}
|
||||
|
||||
fn set_alarm_callback(
|
||||
&self,
|
||||
alarm: embassy_time::driver::AlarmHandle,
|
||||
callback: fn(*mut ()),
|
||||
ctx: *mut (),
|
||||
) {
|
||||
critical_section::with(|cs| {
|
||||
let alarm = unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) };
|
||||
alarm.callback.set(callback as *const ());
|
||||
alarm.ctx.set(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: embassy_time::driver::AlarmHandle, timestamp: u64) -> bool {
|
||||
self.set_alarm(alarm, timestamp)
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
|
||||
use super::AlarmState;
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
peripherals,
|
||||
systimer::{Alarm, SystemTimer, Target},
|
||||
};
|
||||
|
||||
pub const ALARM_COUNT: usize = 3;
|
||||
|
||||
pub type TimerType = SystemTimer<'static>;
|
||||
|
||||
pub struct EmbassyTimer {
|
||||
pub(crate) alarms: Mutex<[AlarmState; ALARM_COUNT]>,
|
||||
pub(crate) alarm0: Alarm<Target, 0>,
|
||||
pub(crate) alarm1: Alarm<Target, 1>,
|
||||
pub(crate) alarm2: Alarm<Target, 2>,
|
||||
}
|
||||
|
||||
const ALARM_STATE_NONE: AlarmState = AlarmState::new();
|
||||
|
||||
embassy_time::time_driver_impl!(static DRIVER: EmbassyTimer = EmbassyTimer {
|
||||
alarms: Mutex::new([ALARM_STATE_NONE; ALARM_COUNT]),
|
||||
alarm0: unsafe { Alarm::<_, 0>::conjure() },
|
||||
alarm1: unsafe { Alarm::<_, 1>::conjure() },
|
||||
alarm2: unsafe { Alarm::<_, 2>::conjure() },
|
||||
});
|
||||
|
||||
impl EmbassyTimer {
|
||||
pub(crate) fn now() -> u64 {
|
||||
SystemTimer::now()
|
||||
}
|
||||
|
||||
pub(crate) fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
// safety:
|
||||
// - we can ignore the possiblity 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
|
||||
let f: fn(*mut ()) = unsafe { core::mem::transmute(alarm.callback.get()) };
|
||||
f(alarm.ctx.get());
|
||||
}
|
||||
|
||||
fn on_interrupt(&self, id: u8) {
|
||||
match id {
|
||||
0 => self.alarm0.clear_interrupt(),
|
||||
1 => self.alarm1.clear_interrupt(),
|
||||
2 => self.alarm2.clear_interrupt(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
critical_section::with(|cs| {
|
||||
self.trigger_alarm(id as usize, cs);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(_clocks: &Clocks, _systimer: TimerType) {
|
||||
use crate::{interrupt, interrupt::Priority, macros::interrupt};
|
||||
|
||||
interrupt::enable(peripherals::Interrupt::SYSTIMER_TARGET0, Priority::max()).unwrap();
|
||||
interrupt::enable(peripherals::Interrupt::SYSTIMER_TARGET1, Priority::max()).unwrap();
|
||||
interrupt::enable(peripherals::Interrupt::SYSTIMER_TARGET2, Priority::max()).unwrap();
|
||||
|
||||
#[interrupt]
|
||||
fn SYSTIMER_TARGET0() {
|
||||
DRIVER.on_interrupt(0);
|
||||
}
|
||||
#[interrupt]
|
||||
fn SYSTIMER_TARGET1() {
|
||||
DRIVER.on_interrupt(1);
|
||||
}
|
||||
#[interrupt]
|
||||
fn SYSTIMER_TARGET2() {
|
||||
DRIVER.on_interrupt(2);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_alarm(
|
||||
&self,
|
||||
alarm: embassy_time::driver::AlarmHandle,
|
||||
timestamp: u64,
|
||||
) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let now = Self::now();
|
||||
let alarm_state = unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) };
|
||||
if timestamp < now {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
self.disable_interrupt(alarm.id());
|
||||
alarm_state.timestamp.set(u64::MAX);
|
||||
return false;
|
||||
}
|
||||
alarm_state.timestamp.set(timestamp);
|
||||
match alarm.id() {
|
||||
0 => {
|
||||
self.alarm0.set_target(timestamp);
|
||||
self.alarm0.interrupt_enable(true);
|
||||
}
|
||||
1 => {
|
||||
self.alarm1.set_target(timestamp);
|
||||
self.alarm1.interrupt_enable(true);
|
||||
}
|
||||
2 => {
|
||||
self.alarm2.set_target(timestamp);
|
||||
self.alarm2.interrupt_enable(true);
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fn disable_interrupt(&self, id: u8) {
|
||||
match id {
|
||||
0 => self.alarm0.interrupt_enable(false),
|
||||
1 => self.alarm1.interrupt_enable(false),
|
||||
2 => self.alarm2.interrupt_enable(false),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use peripherals::TIMG0;
|
||||
|
||||
use super::AlarmState;
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
peripherals,
|
||||
prelude::*,
|
||||
timer::{Timer, Timer0},
|
||||
};
|
||||
|
||||
pub const ALARM_COUNT: usize = 1;
|
||||
|
||||
pub type TimerInner = Timer0<TIMG0>;
|
||||
pub type TimerType = Timer<TimerInner>;
|
||||
|
||||
pub struct EmbassyTimer {
|
||||
pub(crate) alarms: Mutex<[AlarmState; ALARM_COUNT]>,
|
||||
}
|
||||
|
||||
const ALARM_STATE_NONE: AlarmState = AlarmState::new();
|
||||
|
||||
embassy_time::time_driver_impl!(static DRIVER: EmbassyTimer = EmbassyTimer {
|
||||
alarms: Mutex::new([ALARM_STATE_NONE; ALARM_COUNT]),
|
||||
});
|
||||
|
||||
impl EmbassyTimer {
|
||||
pub(crate) fn now() -> u64 {
|
||||
unsafe { TimerInner::steal() }.now()
|
||||
}
|
||||
|
||||
pub(crate) fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
// safety:
|
||||
// - we can ignore the possiblity 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
|
||||
let f: fn(*mut ()) = unsafe { core::mem::transmute(alarm.callback.get()) };
|
||||
f(alarm.ctx.get());
|
||||
}
|
||||
|
||||
fn on_interrupt(&self, id: u8) {
|
||||
critical_section::with(|cs| unsafe {
|
||||
TimerInner::steal().clear_interrupt();
|
||||
self.trigger_alarm(id as usize, cs);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init(clocks: &Clocks, mut timer: TimerType) {
|
||||
use crate::{interrupt, interrupt::Priority};
|
||||
|
||||
// set divider to get a 1mhz clock. abp (80mhz) / 80 = 1mhz... // TODO assert
|
||||
// abp clock is the source and its at the correct speed for the divider
|
||||
timer.set_divider(clocks.apb_clock.to_MHz() as u16);
|
||||
|
||||
interrupt::enable(peripherals::Interrupt::TG0_T0_LEVEL, Priority::max()).unwrap();
|
||||
|
||||
#[interrupt]
|
||||
fn TG0_T0_LEVEL() {
|
||||
DRIVER.on_interrupt(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_alarm(
|
||||
&self,
|
||||
alarm: embassy_time::driver::AlarmHandle,
|
||||
timestamp: u64,
|
||||
) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let now = Self::now();
|
||||
let alarm_state = unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) };
|
||||
let mut tg = unsafe { TimerInner::steal() };
|
||||
if timestamp < now {
|
||||
tg.unlisten();
|
||||
alarm_state.timestamp.set(u64::MAX);
|
||||
return false;
|
||||
}
|
||||
alarm_state.timestamp.set(timestamp);
|
||||
|
||||
tg.load_alarm_value(timestamp);
|
||||
tg.listen();
|
||||
tg.set_counter_decrementing(false);
|
||||
tg.set_auto_reload(false);
|
||||
tg.set_counter_active(true);
|
||||
tg.set_alarm_active(true);
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@
|
||||
#[cfg(riscv)]
|
||||
pub use riscv::*;
|
||||
#[cfg(xtensa)]
|
||||
pub use xtensa::*;
|
||||
|
||||
#[cfg(riscv)]
|
||||
mod riscv;
|
||||
#[cfg(xtensa)]
|
||||
mod xtensa;
|
||||
@ -1,841 +0,0 @@
|
||||
//! Interrupt handling - RISCV
|
||||
//!
|
||||
//! When the `vectored` feature is enabled, CPU interrupts 1 through 15 are
|
||||
//! reserved for each of the possible interrupt priorities.
|
||||
//!
|
||||
//! On chips with a PLIC CPU interrupts 1,2,5,6,9 .. 19 are used.
|
||||
//!
|
||||
//! ```rust
|
||||
//! interrupt1() => Priority::Priority1
|
||||
//! interrupt2() => Priority::Priority2
|
||||
//! ...
|
||||
//! interrupt15() => Priority::Priority15
|
||||
//! ```
|
||||
|
||||
use esp_riscv_rt::riscv::register::{mcause, mepc, mtvec};
|
||||
pub use esp_riscv_rt::TrapFrame;
|
||||
|
||||
#[cfg(not(plic))]
|
||||
pub use self::classic::*;
|
||||
#[cfg(plic)]
|
||||
pub use self::plic::*;
|
||||
use crate::{
|
||||
peripherals::{self, Interrupt},
|
||||
Cpu,
|
||||
};
|
||||
|
||||
// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in
|
||||
// general. However this makes things like preemtive multitasking easier in
|
||||
// future
|
||||
extern "C" {
|
||||
fn interrupt1(frame: &mut TrapFrame);
|
||||
fn interrupt2(frame: &mut TrapFrame);
|
||||
fn interrupt3(frame: &mut TrapFrame);
|
||||
fn interrupt4(frame: &mut TrapFrame);
|
||||
fn interrupt5(frame: &mut TrapFrame);
|
||||
fn interrupt6(frame: &mut TrapFrame);
|
||||
fn interrupt7(frame: &mut TrapFrame);
|
||||
fn interrupt8(frame: &mut TrapFrame);
|
||||
fn interrupt9(frame: &mut TrapFrame);
|
||||
fn interrupt10(frame: &mut TrapFrame);
|
||||
fn interrupt11(frame: &mut TrapFrame);
|
||||
fn interrupt12(frame: &mut TrapFrame);
|
||||
fn interrupt13(frame: &mut TrapFrame);
|
||||
fn interrupt14(frame: &mut TrapFrame);
|
||||
fn interrupt15(frame: &mut TrapFrame);
|
||||
fn interrupt16(frame: &mut TrapFrame);
|
||||
fn interrupt17(frame: &mut TrapFrame);
|
||||
fn interrupt18(frame: &mut TrapFrame);
|
||||
fn interrupt19(frame: &mut TrapFrame);
|
||||
fn interrupt20(frame: &mut TrapFrame);
|
||||
fn interrupt21(frame: &mut TrapFrame);
|
||||
fn interrupt22(frame: &mut TrapFrame);
|
||||
fn interrupt23(frame: &mut TrapFrame);
|
||||
fn interrupt24(frame: &mut TrapFrame);
|
||||
fn interrupt25(frame: &mut TrapFrame);
|
||||
fn interrupt26(frame: &mut TrapFrame);
|
||||
fn interrupt27(frame: &mut TrapFrame);
|
||||
fn interrupt28(frame: &mut TrapFrame);
|
||||
fn interrupt29(frame: &mut TrapFrame);
|
||||
fn interrupt30(frame: &mut TrapFrame);
|
||||
fn interrupt31(frame: &mut TrapFrame);
|
||||
}
|
||||
|
||||
/// Interrupt kind
|
||||
pub enum InterruptKind {
|
||||
/// Level interrupt
|
||||
Level,
|
||||
/// Edge interrupt
|
||||
Edge,
|
||||
}
|
||||
|
||||
/// Enumeration of available CPU interrupts.
|
||||
/// It is possible to create a handler for each of the interrupts. (e.g.
|
||||
/// `interrupt3`)
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum CpuInterrupt {
|
||||
Interrupt1 = 1,
|
||||
Interrupt2,
|
||||
Interrupt3,
|
||||
Interrupt4,
|
||||
Interrupt5,
|
||||
Interrupt6,
|
||||
Interrupt7,
|
||||
Interrupt8,
|
||||
Interrupt9,
|
||||
Interrupt10,
|
||||
Interrupt11,
|
||||
Interrupt12,
|
||||
Interrupt13,
|
||||
Interrupt14,
|
||||
Interrupt15,
|
||||
Interrupt16,
|
||||
Interrupt17,
|
||||
Interrupt18,
|
||||
Interrupt19,
|
||||
Interrupt20,
|
||||
Interrupt21,
|
||||
Interrupt22,
|
||||
Interrupt23,
|
||||
Interrupt24,
|
||||
Interrupt25,
|
||||
Interrupt26,
|
||||
Interrupt27,
|
||||
Interrupt28,
|
||||
Interrupt29,
|
||||
Interrupt30,
|
||||
Interrupt31,
|
||||
}
|
||||
|
||||
/// Interrupt priority levels.
|
||||
#[repr(u8)]
|
||||
pub enum Priority {
|
||||
None = 0,
|
||||
Priority1,
|
||||
Priority2,
|
||||
Priority3,
|
||||
Priority4,
|
||||
Priority5,
|
||||
Priority6,
|
||||
Priority7,
|
||||
Priority8,
|
||||
Priority9,
|
||||
Priority10,
|
||||
Priority11,
|
||||
Priority12,
|
||||
Priority13,
|
||||
Priority14,
|
||||
Priority15,
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
pub fn max() -> Priority {
|
||||
Priority::Priority15
|
||||
}
|
||||
|
||||
pub fn min() -> Priority {
|
||||
Priority::Priority1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vectored")]
|
||||
pub use vectored::*;
|
||||
|
||||
#[cfg(feature = "vectored")]
|
||||
mod vectored {
|
||||
use procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Setup interrupts ready for vectoring
|
||||
#[doc(hidden)]
|
||||
pub(crate) unsafe fn init_vectoring() {
|
||||
for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() {
|
||||
set_kind(
|
||||
crate::get_core(),
|
||||
core::mem::transmute(*num as u32),
|
||||
InterruptKind::Level,
|
||||
);
|
||||
set_priority(
|
||||
crate::get_core(),
|
||||
core::mem::transmute(*num as u32),
|
||||
core::mem::transmute((prio as u8) + 1),
|
||||
);
|
||||
enable_cpu_interrupt(core::mem::transmute(*num as u32));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the interrupts configured for the core
|
||||
#[inline]
|
||||
fn get_configured_interrupts(_core: Cpu, mut status: u128) -> [u128; 16] {
|
||||
unsafe {
|
||||
let mut prios = [0u128; 16];
|
||||
|
||||
while status != 0 {
|
||||
let interrupt_nr = status.trailing_zeros() as u16;
|
||||
// safety: cast is safe because of repr(u16)
|
||||
let cpu_interrupt: CpuInterrupt =
|
||||
get_assigned_cpu_interrupt(core::mem::transmute(interrupt_nr as u16));
|
||||
let prio = get_priority(cpu_interrupt);
|
||||
|
||||
prios[prio as usize] |= 1 << (interrupt_nr as usize);
|
||||
status &= !(1u128 << interrupt_nr);
|
||||
}
|
||||
|
||||
prios
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt Error
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
InvalidInterruptPriority,
|
||||
}
|
||||
|
||||
/// Enables a interrupt at a given priority
|
||||
///
|
||||
/// Note that interrupts still need to be enabled globally for interrupts
|
||||
/// to be serviced.
|
||||
pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
|
||||
if matches!(level, Priority::None) {
|
||||
return Err(Error::InvalidInterruptPriority);
|
||||
}
|
||||
unsafe {
|
||||
let cpu_interrupt =
|
||||
core::mem::transmute(PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32);
|
||||
map(crate::get_core(), interrupt, cpu_interrupt);
|
||||
enable_cpu_interrupt(cpu_interrupt);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[ram]
|
||||
unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
|
||||
let status = get_status(crate::get_core());
|
||||
|
||||
// this has no effect on level interrupts, but the interrupt may be an edge one
|
||||
// so we clear it anyway
|
||||
clear(crate::get_core(), cpu_intr);
|
||||
|
||||
let configured_interrupts = get_configured_interrupts(crate::get_core(), status);
|
||||
let mut interrupt_mask =
|
||||
status & configured_interrupts[INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1]];
|
||||
while interrupt_mask != 0 {
|
||||
let interrupt_nr = interrupt_mask.trailing_zeros();
|
||||
// Interrupt::try_from can fail if interrupt already de-asserted:
|
||||
// silently ignore
|
||||
if let Ok(interrupt) = peripherals::Interrupt::try_from(interrupt_nr as u8) {
|
||||
handle_interrupt(interrupt, context)
|
||||
}
|
||||
interrupt_mask &= !(1u128 << interrupt_nr);
|
||||
}
|
||||
}
|
||||
|
||||
#[ram]
|
||||
unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) {
|
||||
extern "C" {
|
||||
// defined in each hal
|
||||
fn EspDefaultHandler(interrupt: Interrupt);
|
||||
}
|
||||
let handler = peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
|
||||
if handler as *const _ == EspDefaultHandler as *const unsafe extern "C" fn() {
|
||||
EspDefaultHandler(interrupt);
|
||||
} else {
|
||||
let handler: fn(&mut TrapFrame) = core::mem::transmute(handler);
|
||||
handler(save_frame);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt1(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt1, context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt2(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt2, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt3(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt3, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt4(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt4, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt5(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt5, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt6(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt6, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt7(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt7, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt8(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt8, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt9(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt9, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt10(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt10, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt11(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt11, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt12(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt12, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt13(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt13, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt14(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt14, context)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt15(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt15, context)
|
||||
}
|
||||
#[cfg(plic)]
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt16(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt16, context)
|
||||
}
|
||||
#[cfg(plic)]
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt17(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt17, context)
|
||||
}
|
||||
#[cfg(plic)]
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt18(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt18, context)
|
||||
}
|
||||
#[cfg(plic)]
|
||||
#[no_mangle]
|
||||
#[ram]
|
||||
pub unsafe fn interrupt19(context: &mut TrapFrame) {
|
||||
handle_interrupts(CpuInterrupt::Interrupt19, context)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is called from an assembly trap handler.
|
||||
#[doc(hidden)]
|
||||
#[link_section = ".trap.rust"]
|
||||
#[export_name = "_start_trap_rust_hal"]
|
||||
pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
|
||||
extern "C" {
|
||||
// defined in riscv-rt
|
||||
pub fn DefaultHandler();
|
||||
}
|
||||
|
||||
let cause = mcause::read();
|
||||
if cause.is_exception() {
|
||||
let pc = mepc::read();
|
||||
handle_exception(pc, trap_frame);
|
||||
} else {
|
||||
#[cfg(feature = "interrupt-preemption")]
|
||||
let interrupt_priority = handle_priority();
|
||||
let code = mcause::read().code();
|
||||
match code {
|
||||
1 => interrupt1(trap_frame.as_mut().unwrap()),
|
||||
2 => interrupt2(trap_frame.as_mut().unwrap()),
|
||||
3 => interrupt3(trap_frame.as_mut().unwrap()),
|
||||
4 => interrupt4(trap_frame.as_mut().unwrap()),
|
||||
5 => interrupt5(trap_frame.as_mut().unwrap()),
|
||||
6 => interrupt6(trap_frame.as_mut().unwrap()),
|
||||
7 => interrupt7(trap_frame.as_mut().unwrap()),
|
||||
8 => interrupt8(trap_frame.as_mut().unwrap()),
|
||||
9 => interrupt9(trap_frame.as_mut().unwrap()),
|
||||
10 => interrupt10(trap_frame.as_mut().unwrap()),
|
||||
11 => interrupt11(trap_frame.as_mut().unwrap()),
|
||||
12 => interrupt12(trap_frame.as_mut().unwrap()),
|
||||
13 => interrupt13(trap_frame.as_mut().unwrap()),
|
||||
14 => interrupt14(trap_frame.as_mut().unwrap()),
|
||||
15 => interrupt15(trap_frame.as_mut().unwrap()),
|
||||
16 => interrupt16(trap_frame.as_mut().unwrap()),
|
||||
17 => interrupt17(trap_frame.as_mut().unwrap()),
|
||||
18 => interrupt18(trap_frame.as_mut().unwrap()),
|
||||
19 => interrupt19(trap_frame.as_mut().unwrap()),
|
||||
20 => interrupt20(trap_frame.as_mut().unwrap()),
|
||||
21 => interrupt21(trap_frame.as_mut().unwrap()),
|
||||
22 => interrupt22(trap_frame.as_mut().unwrap()),
|
||||
23 => interrupt23(trap_frame.as_mut().unwrap()),
|
||||
24 => interrupt24(trap_frame.as_mut().unwrap()),
|
||||
25 => interrupt25(trap_frame.as_mut().unwrap()),
|
||||
26 => interrupt26(trap_frame.as_mut().unwrap()),
|
||||
27 => interrupt27(trap_frame.as_mut().unwrap()),
|
||||
28 => interrupt28(trap_frame.as_mut().unwrap()),
|
||||
29 => interrupt29(trap_frame.as_mut().unwrap()),
|
||||
30 => interrupt30(trap_frame.as_mut().unwrap()),
|
||||
31 => interrupt31(trap_frame.as_mut().unwrap()),
|
||||
_ => DefaultHandler(),
|
||||
};
|
||||
#[cfg(feature = "interrupt-preemption")]
|
||||
restore_priority(interrupt_priority);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply atomic emulation if needed. Call the default exception handler
|
||||
/// otherwise.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is called from an trap handler.
|
||||
#[doc(hidden)]
|
||||
unsafe fn handle_exception(pc: usize, trap_frame: *mut TrapFrame) {
|
||||
let insn: usize = *(pc as *const _);
|
||||
let needs_atomic_emulation = (insn & 0b1111111) == 0b0101111;
|
||||
if !needs_atomic_emulation {
|
||||
extern "C" {
|
||||
fn ExceptionHandler(tf: *mut TrapFrame);
|
||||
}
|
||||
ExceptionHandler(trap_frame);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let mut frame = [
|
||||
0,
|
||||
(*trap_frame).ra,
|
||||
(*trap_frame).sp,
|
||||
(*trap_frame).gp,
|
||||
(*trap_frame).tp,
|
||||
(*trap_frame).t0,
|
||||
(*trap_frame).t1,
|
||||
(*trap_frame).t2,
|
||||
(*trap_frame).s0,
|
||||
(*trap_frame).s1,
|
||||
(*trap_frame).a0,
|
||||
(*trap_frame).a1,
|
||||
(*trap_frame).a2,
|
||||
(*trap_frame).a3,
|
||||
(*trap_frame).a4,
|
||||
(*trap_frame).a5,
|
||||
(*trap_frame).a6,
|
||||
(*trap_frame).a7,
|
||||
(*trap_frame).s2,
|
||||
(*trap_frame).s3,
|
||||
(*trap_frame).s4,
|
||||
(*trap_frame).s5,
|
||||
(*trap_frame).s6,
|
||||
(*trap_frame).s7,
|
||||
(*trap_frame).s8,
|
||||
(*trap_frame).s9,
|
||||
(*trap_frame).s10,
|
||||
(*trap_frame).s11,
|
||||
(*trap_frame).t3,
|
||||
(*trap_frame).t4,
|
||||
(*trap_frame).t5,
|
||||
(*trap_frame).t6,
|
||||
];
|
||||
riscv_atomic_emulation_trap::atomic_emulation((*trap_frame).pc, &mut frame);
|
||||
|
||||
(*trap_frame).ra = frame[1];
|
||||
(*trap_frame).sp = frame[2];
|
||||
(*trap_frame).gp = frame[3];
|
||||
(*trap_frame).tp = frame[4];
|
||||
(*trap_frame).t0 = frame[5];
|
||||
(*trap_frame).t1 = frame[6];
|
||||
(*trap_frame).t2 = frame[7];
|
||||
(*trap_frame).s0 = frame[8];
|
||||
(*trap_frame).s1 = frame[9];
|
||||
(*trap_frame).a0 = frame[10];
|
||||
(*trap_frame).a1 = frame[11];
|
||||
(*trap_frame).a2 = frame[12];
|
||||
(*trap_frame).a3 = frame[13];
|
||||
(*trap_frame).a4 = frame[14];
|
||||
(*trap_frame).a5 = frame[15];
|
||||
(*trap_frame).a6 = frame[16];
|
||||
(*trap_frame).a7 = frame[17];
|
||||
(*trap_frame).s2 = frame[18];
|
||||
(*trap_frame).s3 = frame[19];
|
||||
(*trap_frame).s4 = frame[20];
|
||||
(*trap_frame).s5 = frame[21];
|
||||
(*trap_frame).s6 = frame[22];
|
||||
(*trap_frame).s7 = frame[23];
|
||||
(*trap_frame).s8 = frame[24];
|
||||
(*trap_frame).s9 = frame[25];
|
||||
(*trap_frame).s10 = frame[26];
|
||||
(*trap_frame).s11 = frame[27];
|
||||
(*trap_frame).t3 = frame[28];
|
||||
(*trap_frame).t4 = frame[29];
|
||||
(*trap_frame).t5 = frame[30];
|
||||
(*trap_frame).t6 = frame[31];
|
||||
(*trap_frame).pc = pc + 4;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[no_mangle]
|
||||
pub fn _setup_interrupts() {
|
||||
extern "C" {
|
||||
static _vector_table: *const u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// disable all known interrupts
|
||||
// at least after the 2nd stage bootloader there are some interrupts enabled
|
||||
// (e.g. UART)
|
||||
for peripheral_interrupt in 0..255 {
|
||||
crate::soc::peripherals::Interrupt::try_from(peripheral_interrupt)
|
||||
.map(|intr| {
|
||||
#[cfg(multi_core)]
|
||||
disable(Cpu::AppCpu, intr);
|
||||
disable(Cpu::ProCpu, intr);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
let vec_table = &_vector_table as *const _ as usize;
|
||||
mtvec::write(vec_table, mtvec::TrapMode::Vectored);
|
||||
|
||||
#[cfg(feature = "vectored")]
|
||||
crate::interrupt::init_vectoring();
|
||||
};
|
||||
|
||||
#[cfg(plic)]
|
||||
unsafe {
|
||||
core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the given peripheral interrupt.
|
||||
pub fn disable(_core: Cpu, interrupt: Interrupt) {
|
||||
unsafe {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
|
||||
|
||||
// set to 0 to disable the peripheral interrupt
|
||||
intr_map_base.offset(interrupt_number).write_volatile(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of peripheral interrupts
|
||||
#[inline]
|
||||
pub fn get_status(_core: Cpu) -> u128 {
|
||||
#[cfg(large_intr_status)]
|
||||
unsafe {
|
||||
((*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_0
|
||||
.read()
|
||||
.bits() as u128)
|
||||
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_1
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 32
|
||||
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.int_status_reg_2
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 64
|
||||
}
|
||||
|
||||
#[cfg(not(large_intr_status))]
|
||||
unsafe {
|
||||
((*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_0
|
||||
.read()
|
||||
.bits() as u128)
|
||||
| ((*crate::peripherals::INTERRUPT_CORE0::PTR)
|
||||
.intr_status_reg_1
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 32
|
||||
}
|
||||
}
|
||||
|
||||
/// Assign a peripheral interrupt to an CPU interrupt.
|
||||
///
|
||||
/// Great care must be taken when using the `vectored` feature (enabled by
|
||||
/// default). Avoid interrupts 1 - 15 when interrupt vectoring is enabled.
|
||||
pub unsafe fn map(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
|
||||
intr_map_base
|
||||
.offset(interrupt_number)
|
||||
.write_volatile(cpu_interrupt_number as u32);
|
||||
}
|
||||
|
||||
/// Get cpu interrupt assigned to peripheral interrupt
|
||||
#[inline]
|
||||
unsafe fn get_assigned_cpu_interrupt(interrupt: Interrupt) -> CpuInterrupt {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
|
||||
|
||||
let cpu_intr = intr_map_base.offset(interrupt_number).read_volatile();
|
||||
|
||||
core::mem::transmute(cpu_intr)
|
||||
}
|
||||
|
||||
#[cfg(not(plic))]
|
||||
mod classic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::Cpu;
|
||||
|
||||
pub(super) const PRIORITY_TO_INTERRUPT: [usize; 15] =
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
pub(super) const INTERRUPT_TO_PRIORITY: [usize; 15] =
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
/// Enable a CPU interrupt
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_enable
|
||||
.modify(|r, w| w.bits((1 << cpu_interrupt_number) | r.bits()));
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// This is safe to call when the `vectored` feature is enabled. The
|
||||
/// vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
unsafe {
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
let cpu_interrupt_number = which as isize;
|
||||
|
||||
let interrupt_type = match kind {
|
||||
InterruptKind::Level => 0,
|
||||
InterruptKind::Edge => 1,
|
||||
};
|
||||
intr.cpu_int_type.modify(|r, w| {
|
||||
w.bits(
|
||||
r.bits() & !(1 << cpu_interrupt_number)
|
||||
| (interrupt_type << cpu_interrupt_number),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the priority level of an CPU interrupt
|
||||
///
|
||||
/// Great care must be taken when using the `vectored` feature (enabled by
|
||||
/// default). Avoid changing the priority of interrupts 1 - 15 when
|
||||
/// interrupt vectoring is enabled.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr_prio_base = intr.cpu_int_pri_0.as_ptr();
|
||||
|
||||
intr_prio_base
|
||||
.offset(cpu_interrupt_number)
|
||||
.write_volatile(priority as u32);
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_clear
|
||||
.write(|w| w.bits(1 << cpu_interrupt_number));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority
|
||||
#[inline]
|
||||
pub(super) unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
let intr_prio_base = intr.cpu_int_pri_0.as_ptr();
|
||||
|
||||
let prio = intr_prio_base
|
||||
.offset(cpu_interrupt as isize)
|
||||
.read_volatile();
|
||||
core::mem::transmute(prio as u8)
|
||||
}
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
use procmacros::ram;
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
#[ram]
|
||||
pub(super) unsafe fn handle_priority() -> u32 {
|
||||
use super::mcause;
|
||||
use crate::riscv;
|
||||
let interrupt_id: usize = mcause::read().code(); // MSB is whether its exception or interrupt.
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
let interrupt_priority = intr
|
||||
.cpu_int_pri_0
|
||||
.as_ptr()
|
||||
.offset(interrupt_id as isize)
|
||||
.read_volatile();
|
||||
|
||||
let prev_interrupt_priority = intr.cpu_int_thresh.read().bits();
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
intr.cpu_int_thresh
|
||||
.write(|w| w.bits(interrupt_priority + 1)); // set the prio threshold to 1 more than current interrupt prio
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority
|
||||
}
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
#[ram]
|
||||
pub(super) unsafe fn restore_priority(stored_prio: u32) {
|
||||
use crate::riscv;
|
||||
unsafe {
|
||||
riscv::interrupt::disable();
|
||||
}
|
||||
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
|
||||
intr.cpu_int_thresh.write(|w| w.bits(stored_prio));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(plic)]
|
||||
mod plic {
|
||||
use super::{CpuInterrupt, InterruptKind, Priority};
|
||||
use crate::Cpu;
|
||||
|
||||
// don't use interrupts reserved for CLIC (0,3,4,7)
|
||||
// for some reason also CPU interrupt 8 doesn't work by default since it's
|
||||
// disabled after reset - so don't use that, too
|
||||
pub(super) const PRIORITY_TO_INTERRUPT: [usize; 15] =
|
||||
[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
||||
|
||||
pub(super) const INTERRUPT_TO_PRIORITY: [usize; 19] = [
|
||||
1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
];
|
||||
|
||||
const DR_REG_PLIC_MX_BASE: u32 = 0x20001000;
|
||||
const PLIC_MXINT_ENABLE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x0;
|
||||
const PLIC_MXINT_TYPE_REG: u32 = DR_REG_PLIC_MX_BASE + 0x4;
|
||||
const PLIC_MXINT_CLEAR_REG: u32 = DR_REG_PLIC_MX_BASE + 0x8;
|
||||
const PLIC_MXINT0_PRI_REG: u32 = DR_REG_PLIC_MX_BASE + 0x10;
|
||||
#[cfg(feature = "interrupt-preemption")]
|
||||
const PLIC_MXINT_THRESH_REG: u32 = DR_REG_PLIC_MX_BASE + 0x90;
|
||||
/// Enable a CPU interrupt
|
||||
pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let mxint_enable = PLIC_MXINT_ENABLE_REG as *mut u32;
|
||||
unsafe {
|
||||
mxint_enable.write_volatile(mxint_enable.read_volatile() | 1 << cpu_interrupt_number);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
|
||||
///
|
||||
/// This is safe to call when the `vectored` feature is enabled. The
|
||||
/// vectored interrupt handler will take care of clearing edge interrupt
|
||||
/// bits.
|
||||
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
|
||||
unsafe {
|
||||
let intr = PLIC_MXINT_TYPE_REG as *mut u32;
|
||||
let cpu_interrupt_number = which as isize;
|
||||
|
||||
let interrupt_type = match kind {
|
||||
InterruptKind::Level => 0,
|
||||
InterruptKind::Edge => 1,
|
||||
};
|
||||
intr.write_volatile(
|
||||
intr.read_volatile() & !(1 << cpu_interrupt_number)
|
||||
| (interrupt_type << cpu_interrupt_number),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the priority level of an CPU interrupt
|
||||
///
|
||||
/// Great care must be taken when using the `vectored` feature (enabled by
|
||||
/// default). Avoid changing the priority of interrupts 1 - 15 when
|
||||
/// interrupt vectoring is enabled.
|
||||
pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
|
||||
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
|
||||
|
||||
let cpu_interrupt_number = which as isize;
|
||||
plic_mxint_pri_ptr
|
||||
.offset(cpu_interrupt_number)
|
||||
.write_volatile(priority as u32);
|
||||
}
|
||||
|
||||
/// Clear a CPU interrupt
|
||||
#[inline]
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr = PLIC_MXINT_CLEAR_REG as *mut u32;
|
||||
intr.write_volatile(1 << cpu_interrupt_number);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt priority
|
||||
#[inline]
|
||||
pub(super) unsafe fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority {
|
||||
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
|
||||
|
||||
let cpu_interrupt_number = cpu_interrupt as isize;
|
||||
let prio = plic_mxint_pri_ptr
|
||||
.offset(cpu_interrupt_number)
|
||||
.read_volatile();
|
||||
core::mem::transmute(prio as u8)
|
||||
}
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
use procmacros::ram;
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
#[ram]
|
||||
pub(super) unsafe fn handle_priority() -> u32 {
|
||||
use super::mcause;
|
||||
use crate::riscv;
|
||||
let plic_mxint_pri_ptr = PLIC_MXINT0_PRI_REG as *mut u32;
|
||||
let interrupt_id: isize = mcause::read().code().try_into().unwrap(); // MSB is whether its exception or interrupt.
|
||||
let interrupt_priority = plic_mxint_pri_ptr.offset(interrupt_id).read_volatile();
|
||||
|
||||
let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32;
|
||||
let prev_interrupt_priority = thresh_reg.read_volatile() & 0x000000FF;
|
||||
// this is a u8 according to esp-idf, so mask everything else.
|
||||
if interrupt_priority < 15 {
|
||||
// leave interrupts disabled if interrupt is of max priority.
|
||||
thresh_reg.write_volatile(interrupt_priority + 1);
|
||||
unsafe {
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
prev_interrupt_priority
|
||||
}
|
||||
#[cfg(all(feature = "interrupt-preemption"))]
|
||||
#[ram]
|
||||
pub(super) unsafe fn restore_priority(stored_prio: u32) {
|
||||
use crate::riscv;
|
||||
unsafe {
|
||||
riscv::interrupt::disable();
|
||||
}
|
||||
let thresh_reg = PLIC_MXINT_THRESH_REG as *mut u32;
|
||||
thresh_reg.write_volatile(stored_prio);
|
||||
}
|
||||
}
|
||||
@ -1,540 +0,0 @@
|
||||
use xtensa_lx::interrupt::{self, InterruptNumber};
|
||||
use xtensa_lx_rt::exception::Context;
|
||||
|
||||
use crate::{
|
||||
peripherals::{self, Interrupt},
|
||||
Cpu,
|
||||
};
|
||||
|
||||
/// Enumeration of available CPU interrupts
|
||||
/// It's possible to create one handler per priority level. (e.g
|
||||
/// `level1_interrupt`)
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum CpuInterrupt {
|
||||
Interrupt0LevelPriority1 = 0,
|
||||
Interrupt1LevelPriority1,
|
||||
Interrupt2LevelPriority1,
|
||||
Interrupt3LevelPriority1,
|
||||
Interrupt4LevelPriority1,
|
||||
Interrupt5LevelPriority1,
|
||||
Interrupt6Timer0Priority1,
|
||||
Interrupt7SoftwarePriority1,
|
||||
Interrupt8LevelPriority1,
|
||||
Interrupt9LevelPriority1,
|
||||
Interrupt10EdgePriority1,
|
||||
Interrupt11ProfilingPriority3,
|
||||
Interrupt12LevelPriority1,
|
||||
Interrupt13LevelPriority1,
|
||||
Interrupt14NmiPriority7,
|
||||
Interrupt15Timer1Priority3,
|
||||
Interrupt16Timer2Priority5,
|
||||
Interrupt17LevelPriority1,
|
||||
Interrupt18LevelPriority1,
|
||||
Interrupt19LevelPriority2,
|
||||
Interrupt20LevelPriority2,
|
||||
Interrupt21LevelPriority2,
|
||||
Interrupt22EdgePriority3,
|
||||
Interrupt23LevelPriority3,
|
||||
Interrupt24LevelPriority4,
|
||||
Interrupt25LevelPriority4,
|
||||
Interrupt26LevelPriority5,
|
||||
Interrupt27LevelPriority3,
|
||||
Interrupt28EdgePriority4,
|
||||
Interrupt29SoftwarePriority3,
|
||||
Interrupt30EdgePriority4,
|
||||
Interrupt31EdgePriority5,
|
||||
}
|
||||
|
||||
/// Assign a peripheral interrupt to an CPU interrupt.
|
||||
///
|
||||
/// Great care **must** be taken when using this function with interrupt
|
||||
/// vectoring (enabled by default). Avoid the following CPU interrupts:
|
||||
/// - Interrupt1LevelPriority1
|
||||
/// - Interrupt19LevelPriority2
|
||||
/// - Interrupt23LevelPriority3
|
||||
/// - Interrupt10EdgePriority1
|
||||
/// - Interrupt22EdgePriority3
|
||||
/// As they are preallocated for interrupt vectoring.
|
||||
///
|
||||
/// Note: this only maps the interrupt to the CPU interrupt. The CPU interrupt
|
||||
/// still needs to be enabled afterwards
|
||||
pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let cpu_interrupt_number = which as isize;
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map.as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map.as_ptr(),
|
||||
};
|
||||
intr_map_base
|
||||
.offset(interrupt_number)
|
||||
.write_volatile(cpu_interrupt_number as u32);
|
||||
}
|
||||
|
||||
/// Disable the given peripheral interrupt.
|
||||
pub fn disable(core: Cpu, interrupt: Interrupt) {
|
||||
unsafe {
|
||||
let interrupt_number = interrupt as isize;
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map.as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map.as_ptr(),
|
||||
};
|
||||
intr_map_base.offset(interrupt_number).write_volatile(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the given CPU interrupt
|
||||
pub fn clear(_core: Cpu, which: CpuInterrupt) {
|
||||
unsafe {
|
||||
xtensa_lx::interrupt::clear(1 << which as u32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of peripheral interrupts
|
||||
pub fn get_status(core: Cpu) -> u128 {
|
||||
unsafe {
|
||||
match core {
|
||||
Cpu::ProCpu => {
|
||||
((*core0_interrupt_peripheral())
|
||||
.pro_intr_status_0
|
||||
.read()
|
||||
.bits() as u128)
|
||||
| ((*core0_interrupt_peripheral())
|
||||
.pro_intr_status_1
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 32
|
||||
| ((*core0_interrupt_peripheral())
|
||||
.pro_intr_status_2
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 64
|
||||
}
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => {
|
||||
((*core1_interrupt_peripheral())
|
||||
.app_intr_status_0
|
||||
.read()
|
||||
.bits() as u128)
|
||||
| ((*core1_interrupt_peripheral())
|
||||
.app_intr_status_1
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 32
|
||||
| ((*core1_interrupt_peripheral())
|
||||
.app_intr_status_2
|
||||
.read()
|
||||
.bits() as u128)
|
||||
<< 64
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
unsafe fn core0_interrupt_peripheral() -> *const crate::peripherals::dport::RegisterBlock {
|
||||
crate::peripherals::DPORT::PTR
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
unsafe fn core1_interrupt_peripheral() -> *const crate::peripherals::dport::RegisterBlock {
|
||||
crate::peripherals::DPORT::PTR
|
||||
}
|
||||
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
unsafe fn core0_interrupt_peripheral() -> *const crate::peripherals::interrupt_core0::RegisterBlock
|
||||
{
|
||||
crate::peripherals::INTERRUPT_CORE0::PTR
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
unsafe fn core1_interrupt_peripheral() -> *const crate::peripherals::interrupt_core1::RegisterBlock
|
||||
{
|
||||
crate::peripherals::INTERRUPT_CORE1::PTR
|
||||
}
|
||||
|
||||
#[cfg(feature = "vectored")]
|
||||
pub use vectored::*;
|
||||
|
||||
#[cfg(feature = "vectored")]
|
||||
mod vectored {
|
||||
use procmacros::ram;
|
||||
|
||||
use super::*;
|
||||
use crate::get_core;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
InvalidInterrupt,
|
||||
}
|
||||
|
||||
/// Interrupt priority levels.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Priority {
|
||||
None = 0,
|
||||
Priority1,
|
||||
Priority2,
|
||||
Priority3,
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
pub fn max() -> Priority {
|
||||
Priority::Priority3
|
||||
}
|
||||
|
||||
pub fn min() -> Priority {
|
||||
Priority::Priority1
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuInterrupt {
|
||||
#[inline]
|
||||
fn level(&self) -> Priority {
|
||||
match self {
|
||||
CpuInterrupt::Interrupt0LevelPriority1
|
||||
| CpuInterrupt::Interrupt1LevelPriority1
|
||||
| CpuInterrupt::Interrupt2LevelPriority1
|
||||
| CpuInterrupt::Interrupt3LevelPriority1
|
||||
| CpuInterrupt::Interrupt4LevelPriority1
|
||||
| CpuInterrupt::Interrupt5LevelPriority1
|
||||
| CpuInterrupt::Interrupt6Timer0Priority1
|
||||
| CpuInterrupt::Interrupt7SoftwarePriority1
|
||||
| CpuInterrupt::Interrupt8LevelPriority1
|
||||
| CpuInterrupt::Interrupt9LevelPriority1
|
||||
| CpuInterrupt::Interrupt10EdgePriority1
|
||||
| CpuInterrupt::Interrupt12LevelPriority1
|
||||
| CpuInterrupt::Interrupt13LevelPriority1
|
||||
| CpuInterrupt::Interrupt17LevelPriority1
|
||||
| CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
|
||||
|
||||
CpuInterrupt::Interrupt19LevelPriority2
|
||||
| CpuInterrupt::Interrupt20LevelPriority2
|
||||
| CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
|
||||
|
||||
CpuInterrupt::Interrupt11ProfilingPriority3
|
||||
| CpuInterrupt::Interrupt15Timer1Priority3
|
||||
| CpuInterrupt::Interrupt22EdgePriority3
|
||||
| CpuInterrupt::Interrupt27LevelPriority3
|
||||
| CpuInterrupt::Interrupt29SoftwarePriority3
|
||||
| CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
|
||||
|
||||
// we direct these to None because we do not support interrupts at this level
|
||||
// through Rust
|
||||
CpuInterrupt::Interrupt24LevelPriority4
|
||||
| CpuInterrupt::Interrupt25LevelPriority4
|
||||
| CpuInterrupt::Interrupt28EdgePriority4
|
||||
| CpuInterrupt::Interrupt30EdgePriority4
|
||||
| CpuInterrupt::Interrupt31EdgePriority5
|
||||
| CpuInterrupt::Interrupt16Timer2Priority5
|
||||
| CpuInterrupt::Interrupt26LevelPriority5
|
||||
| CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the interrupts configured for the core
|
||||
#[inline]
|
||||
fn get_configured_interrupts(core: Cpu, mut status: u128) -> [u128; 8] {
|
||||
unsafe {
|
||||
let intr_map_base = match core {
|
||||
Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map.as_ptr(),
|
||||
#[cfg(multi_core)]
|
||||
Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map.as_ptr(),
|
||||
};
|
||||
|
||||
let mut levels = [0u128; 8];
|
||||
|
||||
while status != 0 {
|
||||
let interrupt_nr = status.trailing_zeros();
|
||||
let i = interrupt_nr as isize;
|
||||
let cpu_interrupt = intr_map_base.offset(i).read_volatile();
|
||||
// safety: cast is safe because of repr(u32)
|
||||
let cpu_interrupt: CpuInterrupt = core::mem::transmute(cpu_interrupt);
|
||||
let level = cpu_interrupt.level() as u8 as usize;
|
||||
|
||||
levels[level] |= 1 << i;
|
||||
status &= !(1u128 << interrupt_nr);
|
||||
}
|
||||
|
||||
levels
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
|
||||
let cpu_interrupt =
|
||||
interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
|
||||
|
||||
unsafe {
|
||||
map(get_core(), interrupt, cpu_interrupt);
|
||||
|
||||
xtensa_lx::interrupt::enable_mask(
|
||||
xtensa_lx::interrupt::get_mask() | 1 << cpu_interrupt as u32,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn interrupt_level_to_cpu_interrupt(
|
||||
level: Priority,
|
||||
is_edge: bool,
|
||||
) -> Result<CpuInterrupt, Error> {
|
||||
Ok(if is_edge {
|
||||
match level {
|
||||
Priority::None => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
|
||||
Priority::Priority2 => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
|
||||
}
|
||||
} else {
|
||||
match level {
|
||||
Priority::None => return Err(Error::InvalidInterrupt),
|
||||
Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
|
||||
Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
|
||||
Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO use CpuInterrupt::LevelX.mask() // TODO make it const
|
||||
const CPU_INTERRUPT_LEVELS: [u32; 8] = [
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000, // Dummy level 0
|
||||
0b_0000_0000_0000_0110_0011_0111_1111_1111, // Level_1
|
||||
0b_0000_0000_0011_1000_0000_0000_0000_0000, // Level 2
|
||||
0b_0010_1000_1100_0000_1000_1000_0000_0000, // Level 3
|
||||
0b_0101_0011_0000_0000_0000_0000_0000_0000, // Level 4
|
||||
0b_1000_0100_0000_0001_0000_0000_0000_0000, // Level 5
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000, // Level 6
|
||||
0b_0000_0000_0000_0000_0100_0000_0000_0000, // Level 7
|
||||
];
|
||||
const CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
|
||||
const CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
|
||||
|
||||
#[inline]
|
||||
fn cpu_interrupt_nr_to_cpu_interrupt_handler(
|
||||
number: u32,
|
||||
) -> Option<unsafe extern "C" fn(u32, save_frame: &mut Context)> {
|
||||
use xtensa_lx_rt::*;
|
||||
// we're fortunate that all esp variants use the same CPU interrupt layout
|
||||
Some(match number {
|
||||
6 => Timer0,
|
||||
7 => Software0,
|
||||
11 => Profiling,
|
||||
14 => NMI,
|
||||
15 => Timer1,
|
||||
16 => Timer2,
|
||||
29 => Software1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_1_interrupt(level: u32, save_frame: &mut Context) {
|
||||
handle_interrupts(level, save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_2_interrupt(level: u32, save_frame: &mut Context) {
|
||||
handle_interrupts(level, save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_3_interrupt(level: u32, save_frame: &mut Context) {
|
||||
handle_interrupts(level, save_frame)
|
||||
}
|
||||
|
||||
#[ram]
|
||||
unsafe fn handle_interrupts(level: u32, save_frame: &mut Context) {
|
||||
let cpu_interrupt_mask =
|
||||
interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[level as usize];
|
||||
|
||||
if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
|
||||
let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
|
||||
let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
|
||||
|
||||
if (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
|
||||
interrupt::clear(1 << cpu_interrupt_nr);
|
||||
}
|
||||
if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
|
||||
handler(level, save_frame);
|
||||
}
|
||||
} else {
|
||||
if (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
|
||||
let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_EDGE;
|
||||
let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
|
||||
interrupt::clear(1 << cpu_interrupt_nr);
|
||||
|
||||
// for edge interrupts cannot rely on the interrupt status
|
||||
// register, therefore call all registered
|
||||
// handlers for current level
|
||||
let interrupt_levels =
|
||||
get_configured_interrupts(crate::get_core(), chip_specific::INTERRUPT_EDGE);
|
||||
let interrupt_mask = interrupt_levels[level as usize];
|
||||
let mut interrupt_mask = interrupt_mask & chip_specific::INTERRUPT_EDGE;
|
||||
loop {
|
||||
let interrupt_nr = interrupt_mask.trailing_zeros();
|
||||
if let Ok(interrupt) = peripherals::Interrupt::try_from(interrupt_nr as u16) {
|
||||
handle_interrupt(level, interrupt, save_frame)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
interrupt_mask &= !(1u128 << interrupt_nr);
|
||||
}
|
||||
} else {
|
||||
// finally check periperal sources and fire of handlers from pac
|
||||
// peripheral mapped interrupts are cleared by the peripheral
|
||||
let status = get_status(crate::get_core());
|
||||
let interrupt_levels = get_configured_interrupts(crate::get_core(), status);
|
||||
let interrupt_mask = status & interrupt_levels[level as usize];
|
||||
let interrupt_nr = interrupt_mask.trailing_zeros();
|
||||
|
||||
// Interrupt::try_from can fail if interrupt already de-asserted:
|
||||
// silently ignore
|
||||
if let Ok(interrupt) = peripherals::Interrupt::try_from(interrupt_nr as u16) {
|
||||
handle_interrupt(level, interrupt, save_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ram]
|
||||
unsafe fn handle_interrupt(level: u32, interrupt: Interrupt, save_frame: &mut Context) {
|
||||
extern "C" {
|
||||
// defined in each hal
|
||||
fn EspDefaultHandler(level: u32, interrupt: Interrupt);
|
||||
}
|
||||
|
||||
let handler = peripherals::__INTERRUPTS[interrupt.number() as usize]._handler;
|
||||
if handler as *const _ == EspDefaultHandler as *const unsafe extern "C" fn() {
|
||||
EspDefaultHandler(level, interrupt);
|
||||
} else {
|
||||
let handler: fn(&mut Context) = core::mem::transmute(handler);
|
||||
handler(save_frame);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
pub const INTERRUPT_EDGE: u128 =
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0000_0000_0011_1111_1100_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0000_0000_0000;
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
use peripherals::Interrupt::*;
|
||||
[
|
||||
TG0_T0_EDGE,
|
||||
TG0_T1_EDGE,
|
||||
TG0_WDT_EDGE,
|
||||
TG0_LACT_EDGE,
|
||||
TG1_T0_EDGE,
|
||||
TG1_T1_EDGE,
|
||||
TG1_WDT_EDGE,
|
||||
TG1_LACT_EDGE,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
pub const INTERRUPT_EDGE: u128 =
|
||||
0b_0000_0000_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0011_1011_1111_1100_0000_0000_0000_0000_0000_0000_0000__0000_0000_0000_0000_0000_0000_0000_0000;
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
|
||||
use peripherals::Interrupt::*;
|
||||
[
|
||||
TG0_T0_EDGE,
|
||||
TG0_T1_EDGE,
|
||||
TG0_WDT_EDGE,
|
||||
TG0_LACT_EDGE,
|
||||
TG1_T0_EDGE,
|
||||
TG1_T1_EDGE,
|
||||
TG1_WDT_EDGE,
|
||||
TG1_LACT_EDGE,
|
||||
SYSTIMER_TARGET0,
|
||||
SYSTIMER_TARGET1,
|
||||
SYSTIMER_TARGET2,
|
||||
]
|
||||
.contains(&interrupt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
mod chip_specific {
|
||||
use super::*;
|
||||
pub const INTERRUPT_EDGE: u128 = 0;
|
||||
#[inline]
|
||||
pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod raw {
|
||||
use super::*;
|
||||
|
||||
extern "C" {
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
fn level1_interrupt(save_frame: &mut Context);
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
fn level2_interrupt(save_frame: &mut Context);
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
fn level3_interrupt(save_frame: &mut Context);
|
||||
fn level4_interrupt(save_frame: &mut Context);
|
||||
fn level5_interrupt(save_frame: &mut Context);
|
||||
fn level6_interrupt(save_frame: &mut Context);
|
||||
fn level7_interrupt(save_frame: &mut Context);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
unsafe fn __level_1_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level1_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
unsafe fn __level_2_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level2_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
#[cfg(not(feature = "vectored"))]
|
||||
unsafe fn __level_3_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level3_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_4_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level4_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_5_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level5_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_6_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level6_interrupt(save_frame)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __level_7_interrupt(_level: u32, save_frame: &mut Context) {
|
||||
level7_interrupt(save_frame)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,168 +0,0 @@
|
||||
//! LEDC (LED PWM Controller) peripheral control
|
||||
//!
|
||||
//! Currently only supports fixed-frequency output. Interrupts are not currently
|
||||
//! implemented. High Speed channels are available for the ESP32 only, while Low
|
||||
//! Speed channels are available for all supported chips.
|
||||
//!
|
||||
//! # LowSpeed Example:
|
||||
//!
|
||||
//! The following will configure the Low Speed Channel0 to 24kHz output with
|
||||
//! 10% duty using the ABPClock
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let mut ledc = LEDC::new(peripherals.LEDC, &clock_control, &mut system.peripheral_clock_control);
|
||||
//! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
|
||||
//!
|
||||
//! let mut lstimer0 = ledc.get_timer::<LowSpeed>(timer::Number::Timer0);
|
||||
//! lstimer0
|
||||
//! .configure(timer::config::Config {
|
||||
//! duty: timer::config::Duty::Duty5Bit,
|
||||
//! clock_source: timer::LSClockSource::APBClk,
|
||||
//! frequency: 24u32.kHz(),
|
||||
//! })
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let mut channel0 = ledc.get_channel(channel::Number::Channel0, led);
|
||||
//! channel0
|
||||
//! .configure(channel::config::Config {
|
||||
//! timer: &lstimer0,
|
||||
//! duty: 10,
|
||||
//! })
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! # HighSpeed Example (ESP32 only):
|
||||
//!
|
||||
//! The following will configure the High Speed Channel0 to 24kHz output with
|
||||
//! 10% duty using the ABPClock
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let ledc = LEDC::new(peripherals.LEDC, &clock_control, &mut system.peripheral_clock_control);
|
||||
//!
|
||||
//! let mut hstimer0 = ledc.get_timer::<HighSpeed>(timer::Number::Timer0);
|
||||
//! hstimer0
|
||||
//! .configure(timer::config::Config {
|
||||
//! duty: timer::config::Duty::Duty5Bit,
|
||||
//! clock_source: timer::HSClockSource::APBClk,
|
||||
//! frequency: 24u32.kHz(),
|
||||
//! })
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let mut channel0 = ledc.get_channel(channel::Number::Channel0, led);
|
||||
//! channel0
|
||||
//! .configure(channel::config::Config {
|
||||
//! timer: &hstimer0,
|
||||
//! duty: 10,
|
||||
//! })
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! # TODO
|
||||
//!
|
||||
//! - Source clock selection
|
||||
//! - Interrupts
|
||||
|
||||
use self::{
|
||||
channel::Channel,
|
||||
timer::{Timer, TimerSpeed},
|
||||
};
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
gpio::OutputPin,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||||
};
|
||||
|
||||
pub mod channel;
|
||||
pub mod timer;
|
||||
|
||||
/// Global slow clock source
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum LSGlobalClkSource {
|
||||
APBClk,
|
||||
}
|
||||
|
||||
/// LEDC (LED PWM Controller)
|
||||
pub struct LEDC<'d> {
|
||||
_instance: PeripheralRef<'d, crate::peripherals::LEDC>,
|
||||
ledc: &'d crate::peripherals::ledc::RegisterBlock,
|
||||
clock_control_config: &'d Clocks<'d>,
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Used to specify HighSpeed Timer/Channel
|
||||
pub struct HighSpeed {}
|
||||
|
||||
/// Used to specify LowSpeed Timer/Channel
|
||||
pub struct LowSpeed {}
|
||||
|
||||
pub trait Speed {}
|
||||
|
||||
#[cfg(esp32)]
|
||||
impl Speed for HighSpeed {}
|
||||
|
||||
impl Speed for LowSpeed {}
|
||||
|
||||
impl<'d> LEDC<'d> {
|
||||
/// Return a new LEDC
|
||||
pub fn new(
|
||||
_instance: impl Peripheral<P = crate::peripherals::LEDC> + 'd,
|
||||
clock_control_config: &'d Clocks,
|
||||
system: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(_instance);
|
||||
system.enable(PeripheralEnable::Ledc);
|
||||
|
||||
let ledc = unsafe { &*crate::peripherals::LEDC::ptr() };
|
||||
LEDC {
|
||||
_instance,
|
||||
ledc,
|
||||
clock_control_config,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set global slow clock source
|
||||
#[cfg(esp32)]
|
||||
pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) {
|
||||
self.ledc.conf.write(|w| w.apb_clk_sel().set_bit());
|
||||
self.ledc.lstimer0_conf.modify(|_, w| w.para_up().set_bit());
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
/// Set global slow clock source
|
||||
pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) {
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let pcr = unsafe { &*crate::peripherals::PCR::ptr() };
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pcr.ledc_sclk_conf.write(|w| w.ledc_sclk_en().set_bit());
|
||||
|
||||
match clock_source {
|
||||
LSGlobalClkSource::APBClk => {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
self.ledc.conf.write(|w| unsafe { w.apb_clk_sel().bits(1) });
|
||||
#[cfg(esp32c6)]
|
||||
pcr.ledc_sclk_conf
|
||||
.write(|w| unsafe { w.ledc_sclk_sel().bits(1) });
|
||||
#[cfg(esp32h2)]
|
||||
pcr.ledc_sclk_conf
|
||||
.write(|w| unsafe { w.ledc_sclk_sel().bits(0) });
|
||||
}
|
||||
}
|
||||
self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit());
|
||||
}
|
||||
|
||||
/// Return a new timer
|
||||
pub fn get_timer<S: TimerSpeed>(&self, number: timer::Number) -> Timer<S> {
|
||||
Timer::new(self.ledc, self.clock_control_config, number)
|
||||
}
|
||||
|
||||
/// Return a new channel
|
||||
pub fn get_channel<S: TimerSpeed, O: OutputPin>(
|
||||
&self,
|
||||
number: channel::Number,
|
||||
output_pin: impl Peripheral<P = O> + 'd,
|
||||
) -> Channel<S, O> {
|
||||
Channel::new(number, output_pin)
|
||||
}
|
||||
}
|
||||
@ -1,448 +0,0 @@
|
||||
use fugit::HertzU32;
|
||||
|
||||
#[cfg(esp32)]
|
||||
use super::HighSpeed;
|
||||
use super::{LowSpeed, Speed};
|
||||
use crate::{clock::Clocks, peripherals::ledc};
|
||||
|
||||
const LEDC_TIMER_DIV_NUM_MAX: u64 = 0x3FFFF;
|
||||
|
||||
/// Timer errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Invalid Divisor
|
||||
Divisor,
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Clock source for HS Timers
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum HSClockSource {
|
||||
APBClk,
|
||||
// TODO RefTick,
|
||||
}
|
||||
|
||||
/// Clock source for LS Timers
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum LSClockSource {
|
||||
APBClk,
|
||||
// TODO SLOWClk
|
||||
}
|
||||
|
||||
/// Timer number
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Number {
|
||||
Timer0,
|
||||
Timer1,
|
||||
Timer2,
|
||||
Timer3,
|
||||
}
|
||||
|
||||
/// Timer configuration
|
||||
pub mod config {
|
||||
use fugit::HertzU32;
|
||||
|
||||
/// Number of bits reserved for duty cycle adjustment
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Duty {
|
||||
Duty1Bit = 1,
|
||||
Duty2Bit,
|
||||
Duty3Bit,
|
||||
Duty4Bit,
|
||||
Duty5Bit,
|
||||
Duty6Bit,
|
||||
Duty7Bit,
|
||||
Duty8Bit,
|
||||
Duty9Bit,
|
||||
Duty10Bit,
|
||||
Duty11Bit,
|
||||
Duty12Bit,
|
||||
Duty13Bit,
|
||||
Duty14Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty15Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty16Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty17Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty18Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty19Bit,
|
||||
#[cfg(esp32)]
|
||||
Duty20Bit,
|
||||
}
|
||||
|
||||
/// Timer configuration
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config<CS> {
|
||||
pub duty: Duty,
|
||||
pub clock_source: CS,
|
||||
pub frequency: HertzU32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait defining the type of timer source
|
||||
pub trait TimerSpeed: Speed {
|
||||
type ClockSourceType;
|
||||
}
|
||||
|
||||
/// Timer source type for LowSpeed timers
|
||||
impl TimerSpeed for LowSpeed {
|
||||
type ClockSourceType = LSClockSource;
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Timer source type for HighSpeed timers
|
||||
impl TimerSpeed for HighSpeed {
|
||||
type ClockSourceType = HSClockSource;
|
||||
}
|
||||
|
||||
/// Interface for Timers
|
||||
pub trait TimerIFace<S: TimerSpeed> {
|
||||
/// Return the frequency of the timer
|
||||
fn get_freq(&self) -> Option<HertzU32>;
|
||||
|
||||
/// Configure the timer
|
||||
fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error>;
|
||||
|
||||
/// Check if the timer has been configured
|
||||
fn is_configured(&self) -> bool;
|
||||
|
||||
/// Return the duty resolution of the timer
|
||||
fn get_duty(&self) -> Option<config::Duty>;
|
||||
|
||||
/// Return the timer number
|
||||
fn get_number(&self) -> Number;
|
||||
|
||||
/// Return the timer frequency, or 0 if not configured
|
||||
fn get_frequency(&self) -> u32;
|
||||
}
|
||||
|
||||
/// Interface for HW configuration of timer
|
||||
pub trait TimerHW<S: TimerSpeed> {
|
||||
/// Get the current source timer frequency from the HW
|
||||
fn get_freq_hw(&self) -> Option<HertzU32>;
|
||||
|
||||
/// Configure the HW for the timer
|
||||
fn configure_hw(&self, divisor: u32);
|
||||
|
||||
/// Update the timer in HW
|
||||
fn update_hw(&self);
|
||||
}
|
||||
|
||||
/// Timer struct
|
||||
pub struct Timer<'a, S: TimerSpeed> {
|
||||
ledc: &'a crate::peripherals::ledc::RegisterBlock,
|
||||
clock_control_config: &'a Clocks<'a>,
|
||||
number: Number,
|
||||
duty: Option<config::Duty>,
|
||||
frequency: u32,
|
||||
configured: bool,
|
||||
use_ref_tick: bool,
|
||||
clock_source: Option<S::ClockSourceType>,
|
||||
}
|
||||
|
||||
impl<'a, S: TimerSpeed> TimerIFace<S> for Timer<'a, S>
|
||||
where
|
||||
Timer<'a, S>: TimerHW<S>,
|
||||
{
|
||||
/// Return the frequency of the timer
|
||||
fn get_freq(&self) -> Option<HertzU32> {
|
||||
self.get_freq_hw()
|
||||
}
|
||||
|
||||
/// Configure the timer
|
||||
fn configure(&mut self, config: config::Config<S::ClockSourceType>) -> Result<(), Error> {
|
||||
self.duty = Some(config.duty);
|
||||
self.clock_source = Some(config.clock_source);
|
||||
|
||||
// TODO: we should return some error here if `unwrap()` fails
|
||||
let src_freq: u32 = self.get_freq().unwrap().to_Hz();
|
||||
let precision = 1 << config.duty as u32;
|
||||
let frequency: u32 = config.frequency.raw();
|
||||
self.frequency = frequency;
|
||||
|
||||
let mut divisor = ((src_freq as u64) << 8) / frequency as u64 / precision as u64;
|
||||
|
||||
if divisor > LEDC_TIMER_DIV_NUM_MAX {
|
||||
// APB_CLK results in divisor which too high. Try using REF_TICK as clock
|
||||
// source.
|
||||
self.use_ref_tick = true;
|
||||
divisor = ((1_000_000 as u64) << 8) / frequency as u64 / precision as u64;
|
||||
}
|
||||
|
||||
if divisor >= LEDC_TIMER_DIV_NUM_MAX || divisor < 256 {
|
||||
return Err(Error::Divisor);
|
||||
}
|
||||
|
||||
self.configure_hw(divisor as u32);
|
||||
self.update_hw();
|
||||
|
||||
self.configured = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if the timer has been configured
|
||||
fn is_configured(&self) -> bool {
|
||||
self.configured
|
||||
}
|
||||
|
||||
/// Return the duty resolution of the timer
|
||||
fn get_duty(&self) -> Option<config::Duty> {
|
||||
self.duty
|
||||
}
|
||||
|
||||
/// Return the timer number
|
||||
fn get_number(&self) -> Number {
|
||||
self.number
|
||||
}
|
||||
|
||||
/// Return the timer frequency
|
||||
fn get_frequency(&self) -> u32 {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: TimerSpeed> Timer<'a, S> {
|
||||
/// Create a new intance of a timer
|
||||
pub fn new(
|
||||
ledc: &'a ledc::RegisterBlock,
|
||||
clock_control_config: &'a Clocks,
|
||||
number: Number,
|
||||
) -> Self {
|
||||
Timer {
|
||||
ledc,
|
||||
clock_control_config,
|
||||
number,
|
||||
duty: None,
|
||||
frequency: 0u32,
|
||||
configured: false,
|
||||
use_ref_tick: false,
|
||||
clock_source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Timer HW implementation for LowSpeed timers
|
||||
impl<'a> TimerHW<LowSpeed> for Timer<'a, LowSpeed> {
|
||||
/// Get the current source timer frequency from the HW
|
||||
fn get_freq_hw(&self) -> Option<fugit::HertzU32> {
|
||||
self.clock_source.map(|cs| match cs {
|
||||
LSClockSource::APBClk => self.clock_control_config.apb_clock,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Configure the HW for the timer
|
||||
fn configure_hw(&self, divisor: u32) {
|
||||
let duty = self.duty.unwrap() as u8;
|
||||
let use_apb = !self.use_ref_tick;
|
||||
|
||||
match self.number {
|
||||
Number::Timer0 => self.ledc.lstimer0_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_apb)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer1 => self.ledc.lstimer1_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_apb)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer2 => self.ledc.lstimer2_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_apb)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_apb)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
/// Configure the HW for the timer
|
||||
fn configure_hw(&self, divisor: u32) {
|
||||
let duty = self.duty.unwrap() as u8;
|
||||
let use_ref_tick = self.use_ref_tick;
|
||||
|
||||
match self.number {
|
||||
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_ref_tick)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.clk_div()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_ref_tick)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.clk_div()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_ref_tick)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.clk_div()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(use_ref_tick)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.clk_div()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Update the timer in HW
|
||||
fn update_hw(&self) {
|
||||
match self.number {
|
||||
Number::Timer0 => self.ledc.lstimer0_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer1 => self.ledc.lstimer1_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer2 => self.ledc.lstimer2_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer3 => self.ledc.lstimer3_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
/// Update the timer in HW
|
||||
fn update_hw(&self) {
|
||||
match self.number {
|
||||
Number::Timer0 => self.ledc.timer0_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer1 => self.ledc.timer1_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer2 => self.ledc.timer2_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
Number::Timer3 => self.ledc.timer3_conf.modify(|_, w| w.para_up().set_bit()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
/// Timer HW implementation for HighSpeed timers
|
||||
impl<'a> TimerHW<HighSpeed> for Timer<'a, HighSpeed> {
|
||||
/// Get the current source timer frequency from the HW
|
||||
fn get_freq_hw(&self) -> Option<HertzU32> {
|
||||
self.clock_source.map(|cs| match cs {
|
||||
// TODO RefTick HSClockSource::RefTick => self.clock_control_config.apb_clock,
|
||||
HSClockSource::APBClk => self.clock_control_config.apb_clock,
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure the HW for the timer
|
||||
fn configure_hw(&self, divisor: u32) {
|
||||
let duty = self.duty.unwrap() as u8;
|
||||
let sel_hstimer = self.clock_source == Some(HSClockSource::APBClk);
|
||||
|
||||
match self.number {
|
||||
Number::Timer0 => self.ledc.hstimer0_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(sel_hstimer)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer1 => self.ledc.hstimer1_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(sel_hstimer)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer2 => self.ledc.hstimer2_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(sel_hstimer)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
Number::Timer3 => self.ledc.hstimer3_conf.modify(|_, w| unsafe {
|
||||
w.tick_sel()
|
||||
.bit(sel_hstimer)
|
||||
.rst()
|
||||
.clear_bit()
|
||||
.pause()
|
||||
.clear_bit()
|
||||
.div_num()
|
||||
.bits(divisor)
|
||||
.duty_res()
|
||||
.bits(duty)
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/// Update the timer in HW
|
||||
fn update_hw(&self) {
|
||||
// Nothing to do for HS timers
|
||||
}
|
||||
}
|
||||
@ -1,302 +0,0 @@
|
||||
//! `no_std` HAL implementations for the peripherals which are common among
|
||||
//! Espressif devices. Implements a number of the traits defined by
|
||||
//! [embedded-hal].
|
||||
//!
|
||||
//! This crate should not be used directly; you should use one of the
|
||||
//! device-specific HAL crates instead:
|
||||
//!
|
||||
//! - [esp32-hal]
|
||||
//! - [esp32c2-hal]
|
||||
//! - [esp32c3-hal]
|
||||
//! - [esp32c6-hal]
|
||||
//! - [esp32h2-hal]
|
||||
//! - [esp32s2-hal]
|
||||
//! - [esp32s3-hal]
|
||||
//!
|
||||
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
|
||||
//! [esp32-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32-hal
|
||||
//! [esp32c2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c2-hal
|
||||
//! [esp32c3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c3-hal
|
||||
//! [esp32c6-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32c6-hal
|
||||
//! [esp32h2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32h2-hal
|
||||
//! [esp32s2-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s2-hal
|
||||
//! [esp32s3-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp32s3-hal
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(xtensa, feature(asm_experimental_arch))]
|
||||
#![cfg_attr(
|
||||
feature = "async",
|
||||
allow(incomplete_features),
|
||||
feature(async_fn_in_trait),
|
||||
feature(impl_trait_projections)
|
||||
)]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
#[cfg(riscv)]
|
||||
pub use esp_riscv_rt::{self, entry, riscv};
|
||||
pub use procmacros as macros;
|
||||
#[cfg(xtensa)]
|
||||
pub use xtensa_lx;
|
||||
#[cfg(xtensa)]
|
||||
pub use xtensa_lx_rt::{self, entry};
|
||||
|
||||
#[cfg(adc)]
|
||||
pub use self::analog::adc::implementation as adc;
|
||||
#[cfg(dac)]
|
||||
pub use self::analog::dac::implementation as dac;
|
||||
#[cfg(any(xtensa, all(riscv, systimer)))]
|
||||
pub use self::delay::Delay;
|
||||
#[cfg(gdma)]
|
||||
pub use self::dma::gdma;
|
||||
#[cfg(pdma)]
|
||||
pub use self::dma::pdma;
|
||||
#[cfg(any(dport, interrupt_core0, interrupt_core1))]
|
||||
pub use self::interrupt::*;
|
||||
#[cfg(rmt)]
|
||||
pub use self::pulse_control::PulseControl;
|
||||
#[cfg(rng)]
|
||||
pub use self::rng::Rng;
|
||||
#[cfg(any(lp_clkrst, rtc_cntl))]
|
||||
pub use self::rtc_cntl::{Rtc, Rwdt};
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
pub use self::soc::cpu_control;
|
||||
#[cfg(efuse)]
|
||||
pub use self::soc::efuse;
|
||||
pub use self::soc::peripherals;
|
||||
#[cfg(any(spi0, spi1, spi2, spi3))]
|
||||
pub use self::spi::Spi;
|
||||
#[cfg(any(timg0, timg1))]
|
||||
pub use self::timer::Timer;
|
||||
#[cfg(any(uart0, uart1, uart2))]
|
||||
pub use self::uart::Uart;
|
||||
#[cfg(usb_device)]
|
||||
pub use self::usb_serial_jtag::UsbSerialJtag;
|
||||
|
||||
#[cfg(aes)]
|
||||
pub mod aes;
|
||||
#[cfg(any(adc, dac))]
|
||||
pub mod analog;
|
||||
#[cfg(assist_debug)]
|
||||
pub mod assist_debug;
|
||||
pub mod clock;
|
||||
#[cfg(any(xtensa, all(riscv, systimer)))]
|
||||
pub mod delay;
|
||||
#[cfg(any(gdma, pdma))]
|
||||
pub mod dma;
|
||||
#[cfg(feature = "embassy")]
|
||||
pub mod embassy;
|
||||
#[cfg(gpio)]
|
||||
pub mod gpio;
|
||||
#[cfg(any(i2c0, i2c1))]
|
||||
pub mod i2c;
|
||||
#[cfg(any(i2s0, i2s1))]
|
||||
pub mod i2s;
|
||||
#[cfg(any(dport, interrupt_core0, interrupt_core1))]
|
||||
pub mod interrupt;
|
||||
#[cfg(ledc)]
|
||||
pub mod ledc;
|
||||
#[cfg(any(mcpwm0, mcpwm1))]
|
||||
pub mod mcpwm;
|
||||
#[cfg(usb0)]
|
||||
pub mod otg_fs;
|
||||
#[cfg(pcnt)]
|
||||
pub mod pcnt;
|
||||
pub mod peripheral;
|
||||
pub mod prelude;
|
||||
#[cfg(rmt)]
|
||||
pub mod pulse_control;
|
||||
#[cfg(radio)]
|
||||
pub mod radio;
|
||||
pub mod reset;
|
||||
#[cfg(rng)]
|
||||
pub mod rng;
|
||||
pub mod rom;
|
||||
#[cfg(rsa)]
|
||||
pub mod rsa;
|
||||
#[cfg(any(lp_clkrst, rtc_cntl))]
|
||||
pub mod rtc_cntl;
|
||||
#[cfg(sha)]
|
||||
pub mod sha;
|
||||
pub mod soc;
|
||||
#[cfg(any(spi0, spi1, spi2, spi3))]
|
||||
pub mod spi;
|
||||
#[cfg(any(dport, pcr, system))]
|
||||
pub mod system;
|
||||
#[cfg(systimer)]
|
||||
pub mod systimer;
|
||||
#[cfg(any(timg0, timg1))]
|
||||
pub mod timer;
|
||||
#[cfg(any(twai0, twai1))]
|
||||
pub mod twai;
|
||||
#[cfg(any(uart0, uart1, uart2))]
|
||||
pub mod uart;
|
||||
#[cfg(usb_device)]
|
||||
pub mod usb_serial_jtag;
|
||||
|
||||
/// State of the CPU saved when entering exception or interrupt
|
||||
pub mod trapframe {
|
||||
#[cfg(riscv)]
|
||||
pub use esp_riscv_rt::TrapFrame;
|
||||
#[cfg(xtensa)]
|
||||
pub use xtensa_lx_rt::exception::Context as TrapFrame;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn EspDefaultHandler(_level: u32, _interrupt: peripherals::Interrupt) {}
|
||||
|
||||
#[cfg(xtensa)]
|
||||
#[no_mangle]
|
||||
extern "C" fn DefaultHandler() {}
|
||||
|
||||
#[cfg(esp32c6)]
|
||||
pub fn disable_apm_filter() {
|
||||
unsafe {
|
||||
(&*esp32c6::LP_APM::PTR).func_ctrl.write(|w| w.bits(0));
|
||||
(&*esp32c6::LP_APM0::PTR).func_ctrl.write(|w| w.bits(0));
|
||||
(&*esp32c6::HP_APM::PTR).func_ctrl.write(|w| w.bits(0));
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of CPU cores
|
||||
/// The actual number of available cores depends on the target.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Cpu {
|
||||
/// The first core
|
||||
ProCpu = 0,
|
||||
/// The second core
|
||||
#[cfg(multi_core)]
|
||||
AppCpu,
|
||||
}
|
||||
|
||||
pub fn get_core() -> Cpu {
|
||||
#[cfg(all(xtensa, multi_core))]
|
||||
match ((xtensa_lx::get_processor_id() >> 13) & 1) != 0 {
|
||||
false => Cpu::ProCpu,
|
||||
true => Cpu::AppCpu,
|
||||
}
|
||||
|
||||
// #[cfg(all(riscv, multi_core))]
|
||||
// TODO get hart_id
|
||||
|
||||
// single core always has ProCpu only
|
||||
#[cfg(single_core)]
|
||||
Cpu::ProCpu
|
||||
}
|
||||
|
||||
mod critical_section_impl {
|
||||
struct CriticalSection;
|
||||
|
||||
critical_section::set_impl!(CriticalSection);
|
||||
|
||||
#[cfg(xtensa)]
|
||||
mod xtensa {
|
||||
unsafe impl critical_section::Impl for super::CriticalSection {
|
||||
unsafe fn acquire() -> critical_section::RawRestoreState {
|
||||
let tkn: critical_section::RawRestoreState;
|
||||
core::arch::asm!("rsil {0}, 5", out(reg) tkn);
|
||||
#[cfg(multi_core)]
|
||||
{
|
||||
let guard = super::multicore::MULTICORE_LOCK.lock();
|
||||
core::mem::forget(guard); // forget it so drop doesn't run
|
||||
}
|
||||
tkn
|
||||
}
|
||||
|
||||
unsafe fn release(token: critical_section::RawRestoreState) {
|
||||
if token != 0 {
|
||||
#[cfg(multi_core)]
|
||||
{
|
||||
debug_assert!(super::multicore::MULTICORE_LOCK.is_owned_by_current_thread());
|
||||
// safety: we logically own the mutex from acquire()
|
||||
super::multicore::MULTICORE_LOCK.force_unlock();
|
||||
}
|
||||
core::arch::asm!(
|
||||
"wsr.ps {0}",
|
||||
"rsync", in(reg) token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(riscv)]
|
||||
mod riscv {
|
||||
use esp_riscv_rt::riscv;
|
||||
|
||||
unsafe impl critical_section::Impl for super::CriticalSection {
|
||||
unsafe fn acquire() -> critical_section::RawRestoreState {
|
||||
let mut mstatus = 0u32;
|
||||
core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus);
|
||||
let interrupts_active = (mstatus & 0b1000) != 0;
|
||||
#[cfg(multi_core)]
|
||||
{
|
||||
let guard = multicore::MULTICORE_LOCK.lock();
|
||||
core::mem::forget(guard); // forget it so drop doesn't run
|
||||
}
|
||||
|
||||
interrupts_active as _
|
||||
}
|
||||
|
||||
unsafe fn release(token: critical_section::RawRestoreState) {
|
||||
if token != 0 {
|
||||
#[cfg(multi_core)]
|
||||
{
|
||||
debug_assert!(multicore::MULTICORE_LOCK.is_owned_by_current_thread());
|
||||
// safety: we logically own the mutex from acquire()
|
||||
multicore::MULTICORE_LOCK.force_unlock();
|
||||
}
|
||||
riscv::interrupt::enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(multi_core)]
|
||||
mod multicore {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use lock_api::{GetThreadId, GuardSend, RawMutex};
|
||||
|
||||
use crate::get_core;
|
||||
|
||||
/// Reentrant Mutex
|
||||
///
|
||||
/// Currently implemented using an atomic spin lock.
|
||||
/// In the future we can optimize this raw mutex to use some hardware
|
||||
/// features.
|
||||
pub(crate) static MULTICORE_LOCK: lock_api::ReentrantMutex<RawSpinlock, RawThreadId, ()> =
|
||||
lock_api::ReentrantMutex::const_new(RawSpinlock::INIT, RawThreadId::INIT, ());
|
||||
|
||||
pub(crate) struct RawThreadId;
|
||||
|
||||
unsafe impl lock_api::GetThreadId for RawThreadId {
|
||||
const INIT: Self = RawThreadId;
|
||||
|
||||
fn nonzero_thread_id(&self) -> core::num::NonZeroUsize {
|
||||
core::num::NonZeroUsize::new((get_core() as usize) + 1).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RawSpinlock(AtomicBool);
|
||||
|
||||
unsafe impl lock_api::RawMutex for RawSpinlock {
|
||||
const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false));
|
||||
|
||||
// A spinlock guard can be sent to another thread and unlocked there
|
||||
type GuardMarker = GuardSend;
|
||||
|
||||
fn lock(&self) {
|
||||
while !self.try_lock() {}
|
||||
}
|
||||
|
||||
fn try_lock(&self) -> bool {
|
||||
self.0
|
||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
unsafe fn unlock(&self) {
|
||||
self.0.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,438 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
gpio::OutputPin,
|
||||
mcpwm::{timer::Timer, PwmPeripheral},
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
};
|
||||
|
||||
/// A MCPWM operator
|
||||
///
|
||||
/// The PWM Operator submodule has the following functions:
|
||||
/// * Generates a PWM signal pair, based on timing references obtained from the
|
||||
/// corresponding PWM timer.
|
||||
/// * Each signal out of the PWM signal pair includes a specific pattern of dead
|
||||
/// time. (Not yet implemented)
|
||||
/// * Superimposes a carrier on the PWM signal, if configured to do so. (Not yet
|
||||
/// implemented)
|
||||
/// * Handles response under fault conditions. (Not yet implemented)
|
||||
pub struct Operator<const OP: u8, PWM> {
|
||||
phantom: PhantomData<PWM>,
|
||||
}
|
||||
|
||||
impl<const OP: u8, PWM: PwmPeripheral> Operator<OP, PWM> {
|
||||
pub(super) fn new() -> Self {
|
||||
// Side note:
|
||||
// It would have been nice to deselect any timer reference on peripheral
|
||||
// initialization.
|
||||
// However experimentation (ESP32-S3) showed that writing `3` to timersel
|
||||
// will not disable the timer reference but instead act as though `2` was
|
||||
// written.
|
||||
Operator {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Select a [`Timer`] to be the timing reference for this operator
|
||||
///
|
||||
/// ### Note:
|
||||
/// By default TIMER0 is used
|
||||
pub fn set_timer<const TIM: u8>(&mut self, timer: &Timer<TIM, PWM>) {
|
||||
let _ = timer;
|
||||
// SAFETY:
|
||||
// We only write to our OPERATORx_TIMERSEL register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
block.operator_timersel.modify(|_, w| match OP {
|
||||
0 => w.operator0_timersel().variant(TIM),
|
||||
1 => w.operator1_timersel().variant(TIM),
|
||||
2 => w.operator2_timersel().variant(TIM),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Use the A output with the given pin and configuration
|
||||
pub fn with_pin_a<'d, Pin: OutputPin>(
|
||||
self,
|
||||
pin: impl Peripheral<P = Pin> + 'd,
|
||||
config: PwmPinConfig<true>,
|
||||
) -> PwmPin<'d, Pin, PWM, OP, true> {
|
||||
PwmPin::new(pin, config)
|
||||
}
|
||||
|
||||
/// Use the B output with the given pin and configuration
|
||||
pub fn with_pin_b<'d, Pin: OutputPin>(
|
||||
self,
|
||||
pin: impl Peripheral<P = Pin> + 'd,
|
||||
config: PwmPinConfig<false>,
|
||||
) -> PwmPin<'d, Pin, PWM, OP, false> {
|
||||
PwmPin::new(pin, config)
|
||||
}
|
||||
|
||||
/// Use both the A and the B output with the given pins and configurations
|
||||
pub fn with_pins<'d, PinA: OutputPin, PinB: OutputPin>(
|
||||
self,
|
||||
pin_a: impl Peripheral<P = PinA> + 'd,
|
||||
config_a: PwmPinConfig<true>,
|
||||
pin_b: impl Peripheral<P = PinB> + 'd,
|
||||
config_b: PwmPinConfig<false>,
|
||||
) -> (
|
||||
PwmPin<'d, PinA, PWM, OP, true>,
|
||||
PwmPin<'d, PinB, PWM, OP, false>,
|
||||
) {
|
||||
(PwmPin::new(pin_a, config_a), PwmPin::new(pin_b, config_b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration describing how the operator generates a signal on a connected
|
||||
/// pin
|
||||
pub struct PwmPinConfig<const IS_A: bool> {
|
||||
actions: PwmActions<IS_A>,
|
||||
update_method: PwmUpdateMethod,
|
||||
}
|
||||
|
||||
impl<const IS_A: bool> PwmPinConfig<IS_A> {
|
||||
/// A configuration using [`PwmActions::UP_ACTIVE_HIGH`] and
|
||||
/// [`PwmUpdateMethod::SYNC_ON_ZERO`]
|
||||
pub const UP_ACTIVE_HIGH: Self =
|
||||
Self::new(PwmActions::UP_ACTIVE_HIGH, PwmUpdateMethod::SYNC_ON_ZERO);
|
||||
/// A configuration using [`PwmActions::UP_DOWN_ACTIVE_HIGH`] and
|
||||
/// [`PwmUpdateMethod::SYNC_ON_ZERO`]
|
||||
pub const UP_DOWN_ACTIVE_HIGH: Self = Self::new(
|
||||
PwmActions::UP_DOWN_ACTIVE_HIGH,
|
||||
PwmUpdateMethod::SYNC_ON_ZERO,
|
||||
);
|
||||
|
||||
/// Get a configuration using the given `PwmActions` and `PwmUpdateMethod`
|
||||
pub const fn new(actions: PwmActions<IS_A>, update_method: PwmUpdateMethod) -> Self {
|
||||
PwmPinConfig {
|
||||
actions,
|
||||
update_method,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A pin driven by an MCPWM operator
|
||||
pub struct PwmPin<'d, Pin, PWM, const OP: u8, const IS_A: bool> {
|
||||
_pin: PeripheralRef<'d, Pin>,
|
||||
phantom: PhantomData<PWM>,
|
||||
}
|
||||
|
||||
impl<'d, Pin: OutputPin, PWM: PwmPeripheral, const OP: u8, const IS_A: bool>
|
||||
PwmPin<'d, Pin, PWM, OP, IS_A>
|
||||
{
|
||||
fn new(pin: impl Peripheral<P = Pin> + 'd, config: PwmPinConfig<IS_A>) -> Self {
|
||||
crate::into_ref!(pin);
|
||||
let output_signal = PWM::output_signal::<OP, IS_A>();
|
||||
pin.enable_output(true)
|
||||
.connect_peripheral_to_output(output_signal);
|
||||
let mut pin = PwmPin {
|
||||
_pin: pin,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
pin.set_actions(config.actions);
|
||||
pin.set_update_method(config.update_method);
|
||||
pin
|
||||
}
|
||||
|
||||
/// Configure what actions should be taken on timing events
|
||||
pub fn set_actions(&mut self, value: PwmActions<IS_A>) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_x register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
|
||||
let bits = value.0;
|
||||
|
||||
// SAFETY:
|
||||
// `bits` is a valid bit pattern
|
||||
unsafe {
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block.gen0_a.write(|w| w.bits(bits)),
|
||||
(1, true) => block.gen1_a.write(|w| w.bits(bits)),
|
||||
(2, true) => block.gen2_a.write(|w| w.bits(bits)),
|
||||
(0, false) => block.gen0_b.write(|w| w.bits(bits)),
|
||||
(1, false) => block.gen1_b.write(|w| w.bits(bits)),
|
||||
(2, false) => block.gen2_b.write(|w| w.bits(bits)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set how a new timestamp syncs with the timer
|
||||
#[cfg(esp32)]
|
||||
pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_x_UPMETHOD register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
let bits = update_method.0;
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block
|
||||
.gen0_stmp_cfg
|
||||
.modify(|_, w| w.gen0_a_upmethod().variant(bits)),
|
||||
(1, true) => block
|
||||
.gen1_stmp_cfg
|
||||
.modify(|_, w| w.gen1_a_upmethod().variant(bits)),
|
||||
(2, true) => block
|
||||
.gen2_stmp_cfg
|
||||
.modify(|_, w| w.gen2_a_upmethod().variant(bits)),
|
||||
(0, false) => block
|
||||
.gen0_stmp_cfg
|
||||
.modify(|_, w| w.gen0_b_upmethod().variant(bits)),
|
||||
(1, false) => block
|
||||
.gen1_stmp_cfg
|
||||
.modify(|_, w| w.gen1_b_upmethod().variant(bits)),
|
||||
(2, false) => block
|
||||
.gen2_stmp_cfg
|
||||
.modify(|_, w| w.gen2_b_upmethod().variant(bits)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set how a new timestamp syncs with the timer
|
||||
#[cfg(esp32s3)]
|
||||
pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_x_UPMETHOD register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
let bits = update_method.0;
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block
|
||||
.cmpr0_cfg
|
||||
.modify(|_, w| w.cmpr0_a_upmethod().variant(bits)),
|
||||
(1, true) => block
|
||||
.cmpr1_cfg
|
||||
.modify(|_, w| w.cmpr1_a_upmethod().variant(bits)),
|
||||
(2, true) => block
|
||||
.cmpr2_cfg
|
||||
.modify(|_, w| w.cmpr2_a_upmethod().variant(bits)),
|
||||
(0, false) => block
|
||||
.cmpr0_cfg
|
||||
.modify(|_, w| w.cmpr0_b_upmethod().variant(bits)),
|
||||
(1, false) => block
|
||||
.cmpr1_cfg
|
||||
.modify(|_, w| w.cmpr1_b_upmethod().variant(bits)),
|
||||
(2, false) => block
|
||||
.cmpr2_cfg
|
||||
.modify(|_, w| w.cmpr2_b_upmethod().variant(bits)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set how a new timestamp syncs with the timer
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pub fn set_update_method(&mut self, update_method: PwmUpdateMethod) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_x_UPMETHOD register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
let bits = update_method.0;
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block
|
||||
.gen0_stmp_cfg
|
||||
.modify(|_, w| w.cmpr0_a_upmethod().variant(bits)),
|
||||
(1, true) => block
|
||||
.gen1_stmp_cfg
|
||||
.modify(|_, w| w.cmpr1_a_upmethod().variant(bits)),
|
||||
(2, true) => block
|
||||
.gen2_stmp_cfg
|
||||
.modify(|_, w| w.cmpr2_a_upmethod().variant(bits)),
|
||||
(0, false) => block
|
||||
.gen0_stmp_cfg
|
||||
.modify(|_, w| w.cmpr0_b_upmethod().variant(bits)),
|
||||
(1, false) => block
|
||||
.gen1_stmp_cfg
|
||||
.modify(|_, w| w.cmpr1_b_upmethod().variant(bits)),
|
||||
(2, false) => block
|
||||
.gen2_stmp_cfg
|
||||
.modify(|_, w| w.cmpr2_b_upmethod().variant(bits)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a new timestamp.
|
||||
/// The written value will take effect according to the set
|
||||
/// [`PwmUpdateMethod`].
|
||||
#[cfg(esp32)]
|
||||
pub fn set_timestamp(&mut self, value: u16) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_TSTMP_x register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block.gen0_tstmp_a.write(|w| w.gen0_a().variant(value)),
|
||||
(1, true) => block.gen1_tstmp_a.write(|w| w.gen1_a().variant(value)),
|
||||
(2, true) => block.gen2_tstmp_a.write(|w| w.gen2_a().variant(value)),
|
||||
(0, false) => block.gen0_tstmp_b.write(|w| w.gen0_b().variant(value)),
|
||||
(1, false) => block.gen1_tstmp_b.write(|w| w.gen1_b().variant(value)),
|
||||
(2, false) => block.gen2_tstmp_b.write(|w| w.gen2_b().variant(value)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a new timestamp.
|
||||
/// The written value will take effect according to the set
|
||||
/// [`PwmUpdateMethod`].
|
||||
#[cfg(esp32s3)]
|
||||
pub fn set_timestamp(&mut self, value: u16) {
|
||||
// SAFETY:
|
||||
// We only write to our CMPRx_VALUEx register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block.cmpr0_value0.write(|w| w.cmpr0_a().variant(value)),
|
||||
(1, true) => block.cmpr1_value0.write(|w| w.cmpr1_a().variant(value)),
|
||||
(2, true) => block.cmpr2_value0.write(|w| w.cmpr2_a().variant(value)),
|
||||
(0, false) => block.cmpr0_value1.write(|w| w.cmpr0_b().variant(value)),
|
||||
(1, false) => block.cmpr1_value1.write(|w| w.cmpr1_b().variant(value)),
|
||||
(2, false) => block.cmpr2_value1.write(|w| w.cmpr2_b().variant(value)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a new timestamp.
|
||||
/// The written value will take effect according to the set
|
||||
/// [`PwmUpdateMethod`].
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pub fn set_timestamp(&mut self, value: u16) {
|
||||
// SAFETY:
|
||||
// We only write to our GENx_TSTMP_x register
|
||||
let block = unsafe { &*PWM::block() };
|
||||
match (OP, IS_A) {
|
||||
(0, true) => block.gen0_tstmp_a.write(|w| w.cmpr0_a().variant(value)),
|
||||
(1, true) => block.gen1_tstmp_a.write(|w| w.cmpr1_a().variant(value)),
|
||||
(2, true) => block.gen2_tstmp_a.write(|w| w.cmpr2_a().variant(value)),
|
||||
(0, false) => block.gen0_tstmp_b.write(|w| w.cmpr0_b().variant(value)),
|
||||
(1, false) => block.gen1_tstmp_b.write(|w| w.cmpr1_b().variant(value)),
|
||||
(2, false) => block.gen2_tstmp_b.write(|w| w.cmpr2_b().variant(value)),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An action the operator applies to an output
|
||||
#[non_exhaustive]
|
||||
#[repr(u32)]
|
||||
pub enum UpdateAction {
|
||||
/// Clear the output by setting it to a low level.
|
||||
SetLow = 1,
|
||||
/// Set the to a high level.
|
||||
SetHigh = 2,
|
||||
/// Change the current output level to the opposite value.
|
||||
/// If it is currently pulled high, pull it low, or vice versa.
|
||||
Toggle = 3,
|
||||
}
|
||||
|
||||
/// Settings for what actions should be taken on timing events
|
||||
///
|
||||
/// ### Note:
|
||||
/// The hardware supports using a timestamp A event to trigger an action on
|
||||
/// output B or vice versa. For clearer ownership semantics this HAL does not
|
||||
/// support such configurations.
|
||||
pub struct PwmActions<const IS_A: bool>(u32);
|
||||
|
||||
impl<const IS_A: bool> PwmActions<IS_A> {
|
||||
/// Using this setting together with a timer configured with
|
||||
/// [`PwmWorkingMode::Increase`](super::timer::PwmWorkingMode::Increase)
|
||||
/// will set the output high for a duration proportional to the set
|
||||
/// timestamp.
|
||||
pub const UP_ACTIVE_HIGH: Self = Self::empty()
|
||||
.on_up_counting_timer_equals_zero(UpdateAction::SetHigh)
|
||||
.on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
|
||||
|
||||
/// Using this setting together with a timer configured with
|
||||
/// [`PwmWorkingMode::UpDown`](super::timer::PwmWorkingMode::UpDown) will
|
||||
/// set the output high for a duration proportional to the set
|
||||
/// timestamp.
|
||||
pub const UP_DOWN_ACTIVE_HIGH: Self = Self::empty()
|
||||
.on_down_counting_timer_equals_timestamp(UpdateAction::SetHigh)
|
||||
.on_up_counting_timer_equals_timestamp(UpdateAction::SetLow);
|
||||
|
||||
/// `PwmActions` with no `UpdateAction`s set
|
||||
pub const fn empty() -> Self {
|
||||
PwmActions(0)
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `UTEZ` event
|
||||
pub const fn on_up_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
|
||||
self.with_value_at_offset(action as u32, 0)
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `UTEP` event
|
||||
pub const fn on_up_counting_timer_equals_period(self, action: UpdateAction) -> Self {
|
||||
self.with_value_at_offset(action as u32, 2)
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `UTEA`/`UTEB` event
|
||||
pub const fn on_up_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
|
||||
match IS_A {
|
||||
true => self.with_value_at_offset(action as u32, 4),
|
||||
false => self.with_value_at_offset(action as u32, 6),
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `DTEZ` event
|
||||
pub const fn on_down_counting_timer_equals_zero(self, action: UpdateAction) -> Self {
|
||||
self.with_value_at_offset(action as u32, 12)
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `DTEP` event
|
||||
pub const fn on_down_counting_timer_equals_period(self, action: UpdateAction) -> Self {
|
||||
self.with_value_at_offset(action as u32, 14)
|
||||
}
|
||||
|
||||
/// Choose an `UpdateAction` for an `DTEA`/`DTEB` event
|
||||
pub const fn on_down_counting_timer_equals_timestamp(self, action: UpdateAction) -> Self {
|
||||
match IS_A {
|
||||
true => self.with_value_at_offset(action as u32, 16),
|
||||
false => self.with_value_at_offset(action as u32, 18),
|
||||
}
|
||||
}
|
||||
|
||||
const fn with_value_at_offset(self, value: u32, offset: u32) -> Self {
|
||||
let mask = !(0b11 << offset);
|
||||
let value = (self.0 & mask) | (value << offset);
|
||||
PwmActions(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for when [`PwmPin::set_timestamp`] takes effect
|
||||
///
|
||||
/// Multiple syncing triggers can be set.
|
||||
pub struct PwmUpdateMethod(u8);
|
||||
|
||||
impl PwmUpdateMethod {
|
||||
/// New timestamp will be applied immediately
|
||||
pub const SYNC_IMMEDIATLY: Self = Self::empty();
|
||||
/// New timestamp will be applied when timer is equal to zero
|
||||
pub const SYNC_ON_ZERO: Self = Self::empty().sync_on_timer_equals_zero();
|
||||
/// New timestamp will be applied when timer is equal to period
|
||||
pub const SYNC_ON_PERIOD: Self = Self::empty().sync_on_timer_equals_period();
|
||||
|
||||
/// `PwmUpdateMethod` with no sync triggers.
|
||||
/// Corresponds to syncing immediately
|
||||
pub const fn empty() -> Self {
|
||||
PwmUpdateMethod(0)
|
||||
}
|
||||
|
||||
/// Enable syncing new timestamp values when timer is equal to zero
|
||||
pub const fn sync_on_timer_equals_zero(mut self) -> Self {
|
||||
self.0 |= 0b0001;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable syncing new timestamp values when timer is equal to period
|
||||
pub const fn sync_on_timer_equals_period(mut self) -> Self {
|
||||
self.0 |= 0b0010;
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
//! USB OTG full-speed peripheral
|
||||
|
||||
pub use esp_synopsys_usb_otg::UsbBus;
|
||||
use esp_synopsys_usb_otg::UsbPeripheral;
|
||||
|
||||
use crate::{
|
||||
gpio::InputSignal,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals,
|
||||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait UsbSel {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait UsbDp {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait UsbDm {}
|
||||
|
||||
pub struct USB<'d, S, P, M>
|
||||
where
|
||||
S: UsbSel + Send + Sync,
|
||||
P: UsbDp + Send + Sync,
|
||||
M: UsbDm + Send + Sync,
|
||||
{
|
||||
_usb0: PeripheralRef<'d, peripherals::USB0>,
|
||||
_usb_sel: PeripheralRef<'d, S>,
|
||||
_usb_dp: PeripheralRef<'d, P>,
|
||||
_usb_dm: PeripheralRef<'d, M>,
|
||||
}
|
||||
|
||||
impl<'d, S, P, M> USB<'d, S, P, M>
|
||||
where
|
||||
S: UsbSel + Send + Sync,
|
||||
P: UsbDp + Send + Sync,
|
||||
M: UsbDm + Send + Sync,
|
||||
{
|
||||
pub fn new(
|
||||
usb0: impl Peripheral<P = peripherals::USB0> + 'd,
|
||||
usb_sel: impl Peripheral<P = S> + 'd,
|
||||
usb_dp: impl Peripheral<P = P> + 'd,
|
||||
usb_dm: impl Peripheral<P = M> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(usb_sel, usb_dp, usb_dm);
|
||||
peripheral_clock_control.enable(PeripheralEnable::Usb);
|
||||
Self {
|
||||
_usb0: usb0.into_ref(),
|
||||
_usb_sel: usb_sel,
|
||||
_usb_dp: usb_dp,
|
||||
_usb_dm: usb_dm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, S, P, M> Sync for USB<'d, S, P, M>
|
||||
where
|
||||
S: UsbSel + Send + Sync,
|
||||
P: UsbDp + Send + Sync,
|
||||
M: UsbDm + Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
unsafe impl<'d, S, P, M> UsbPeripheral for USB<'d, S, P, M>
|
||||
where
|
||||
S: UsbSel + Send + Sync,
|
||||
P: UsbDp + Send + Sync,
|
||||
M: UsbDm + Send + Sync,
|
||||
{
|
||||
const REGISTERS: *const () = peripherals::USB0::ptr() as *const ();
|
||||
|
||||
const HIGH_SPEED: bool = false;
|
||||
const FIFO_DEPTH_WORDS: usize = 256;
|
||||
const ENDPOINT_COUNT: usize = 5;
|
||||
|
||||
fn enable() {
|
||||
unsafe {
|
||||
let usb_wrap = &*peripherals::USB_WRAP::PTR;
|
||||
usb_wrap.otg_conf.modify(|_, w| {
|
||||
w.usb_pad_enable()
|
||||
.set_bit()
|
||||
.phy_sel()
|
||||
.clear_bit()
|
||||
.clk_en()
|
||||
.set_bit()
|
||||
.ahb_clk_force_on()
|
||||
.set_bit()
|
||||
.phy_clk_force_on()
|
||||
.set_bit()
|
||||
});
|
||||
|
||||
#[cfg(esp32s3)]
|
||||
{
|
||||
let rtc = &*peripherals::RTC_CNTL::PTR;
|
||||
rtc.usb_conf
|
||||
.modify(|_, w| w.sw_hw_usb_phy_sel().set_bit().sw_usb_phy_sel().set_bit());
|
||||
}
|
||||
|
||||
crate::gpio::connect_high_to_peripheral(InputSignal::USB_OTG_IDDIG); // connected connector is mini-B side
|
||||
crate::gpio::connect_high_to_peripheral(InputSignal::USB_SRP_BVALID); // HIGH to force USB device mode
|
||||
crate::gpio::connect_high_to_peripheral(InputSignal::USB_OTG_VBUSVALID); // receiving a valid Vbus from device
|
||||
crate::gpio::connect_low_to_peripheral(InputSignal::USB_OTG_AVALID);
|
||||
|
||||
usb_wrap.otg_conf.modify(|_, w| {
|
||||
w.pad_pull_override()
|
||||
.set_bit()
|
||||
.dp_pullup()
|
||||
.set_bit()
|
||||
.dp_pulldown()
|
||||
.clear_bit()
|
||||
.dm_pullup()
|
||||
.clear_bit()
|
||||
.dm_pulldown()
|
||||
.clear_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn ahb_frequency_hz(&self) -> u32 {
|
||||
// unused
|
||||
80_000_000
|
||||
}
|
||||
}
|
||||
@ -1,240 +0,0 @@
|
||||
use super::unit;
|
||||
use crate::{
|
||||
gpio::{InputPin, InputSignal, ONE_INPUT, ZERO_INPUT},
|
||||
peripheral::Peripheral,
|
||||
peripherals::GPIO,
|
||||
};
|
||||
|
||||
/// Channel number
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Number {
|
||||
Channel0,
|
||||
Channel1,
|
||||
}
|
||||
|
||||
/// PCNT channel action on signal edge
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum EdgeMode {
|
||||
/// Hold current count value
|
||||
Hold = 0,
|
||||
/// Increase count value
|
||||
#[default]
|
||||
Increment = 1,
|
||||
/// Decrease count value
|
||||
Decrement = 2,
|
||||
}
|
||||
|
||||
/// PCNT channel action on control level
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum CtrlMode {
|
||||
/// Keep current count mode
|
||||
Keep = 0,
|
||||
/// Invert current count mode (increase -> decrease, decrease -> increase)
|
||||
#[default]
|
||||
Reverse = 1,
|
||||
/// Hold current count value
|
||||
Disable = 2,
|
||||
}
|
||||
|
||||
/// Pulse Counter configuration for a single channel
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub struct Config {
|
||||
/// PCNT low control mode
|
||||
pub lctrl_mode: CtrlMode,
|
||||
/// PCNT high control mode
|
||||
pub hctrl_mode: CtrlMode,
|
||||
/// PCNT signal positive edge count mode
|
||||
pub pos_edge: EdgeMode,
|
||||
/// PCNT signal negative edge count mode
|
||||
pub neg_edge: EdgeMode,
|
||||
pub invert_ctrl: bool,
|
||||
pub invert_sig: bool,
|
||||
}
|
||||
|
||||
/// PcntPin can be always high, always low, or an actual pin
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PcntSource {
|
||||
source: u8,
|
||||
}
|
||||
|
||||
impl PcntSource {
|
||||
pub fn from_pin<'a, P: InputPin>(pin: impl Peripheral<P = P> + 'a) -> Self {
|
||||
crate::into_ref!(pin);
|
||||
Self {
|
||||
source: pin.number(),
|
||||
}
|
||||
}
|
||||
pub fn always_high() -> Self {
|
||||
Self { source: ONE_INPUT }
|
||||
}
|
||||
pub fn always_low() -> Self {
|
||||
Self { source: ZERO_INPUT }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Channel {
|
||||
unit: unit::Number,
|
||||
channel: Number,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
/// return a new Channel
|
||||
pub(super) fn new(unit: unit::Number, channel: Number) -> Self {
|
||||
Self { unit, channel }
|
||||
}
|
||||
|
||||
/// Configure the channel
|
||||
pub fn configure(&mut self, ctrl_signal: PcntSource, edge_signal: PcntSource, config: Config) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match self.unit {
|
||||
unit::Number::Unit0 => &pcnt.u0_conf0,
|
||||
unit::Number::Unit1 => &pcnt.u1_conf0,
|
||||
unit::Number::Unit2 => &pcnt.u2_conf0,
|
||||
unit::Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
match self.channel {
|
||||
Number::Channel0 => {
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.ch0_hctrl_mode()
|
||||
.bits(config.hctrl_mode as u8)
|
||||
.ch0_lctrl_mode()
|
||||
.bits(config.lctrl_mode as u8)
|
||||
.ch0_neg_mode()
|
||||
.bits(config.neg_edge as u8)
|
||||
.ch0_pos_mode()
|
||||
.bits(config.pos_edge as u8)
|
||||
});
|
||||
}
|
||||
Number::Channel1 => {
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.ch1_hctrl_mode()
|
||||
.bits(config.hctrl_mode as u8)
|
||||
.ch1_lctrl_mode()
|
||||
.bits(config.lctrl_mode as u8)
|
||||
.ch1_neg_mode()
|
||||
.bits(config.neg_edge as u8)
|
||||
.ch1_pos_mode()
|
||||
.bits(config.pos_edge as u8)
|
||||
});
|
||||
}
|
||||
}
|
||||
self.set_ctrl_signal(ctrl_signal, config.invert_ctrl);
|
||||
self.set_edge_signal(edge_signal, config.invert_sig);
|
||||
}
|
||||
|
||||
/// Set the control signal (pin/high/low) for this channel
|
||||
pub fn set_ctrl_signal(&self, source: PcntSource, invert: bool) -> &Self {
|
||||
let signal = match self.unit {
|
||||
unit::Number::Unit0 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT0_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT0_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit1 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT1_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT1_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit2 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT2_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT2_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit3 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT3_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT3_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT4_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT4_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT5_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT5_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT6_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT6_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT7_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT7_CTRL_CH1,
|
||||
},
|
||||
};
|
||||
|
||||
if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
|
||||
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
|
||||
w.sel()
|
||||
.set_bit()
|
||||
.in_inv_sel()
|
||||
.bit(invert)
|
||||
.in_sel()
|
||||
.bits(source.source)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the edge signal (pin/high/low) for this channel
|
||||
pub fn set_edge_signal(&self, source: PcntSource, invert: bool) -> &Self {
|
||||
let signal = match self.unit {
|
||||
unit::Number::Unit0 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT0_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT0_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit1 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT1_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT1_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit2 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT2_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT2_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit3 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT3_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT3_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT4_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT4_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT5_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT5_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT6_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT6_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT7_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT7_SIG_CH1,
|
||||
},
|
||||
};
|
||||
|
||||
if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
|
||||
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
|
||||
w.sel()
|
||||
.set_bit()
|
||||
.in_inv_sel()
|
||||
.bit(invert)
|
||||
.in_sel()
|
||||
.bits(source.source)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
use self::unit::Unit;
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
system::PeripheralClockControl,
|
||||
};
|
||||
|
||||
pub mod channel;
|
||||
pub mod unit;
|
||||
|
||||
pub struct PCNT<'d> {
|
||||
_instance: PeripheralRef<'d, crate::peripherals::PCNT>,
|
||||
}
|
||||
|
||||
impl<'d> PCNT<'d> {
|
||||
/// Return a new PCNT
|
||||
pub fn new(
|
||||
_instance: impl Peripheral<P = crate::peripherals::PCNT> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(_instance);
|
||||
// Enable the PCNT peripherals clock in the system peripheral
|
||||
peripheral_clock_control.enable(crate::system::Peripheral::Pcnt);
|
||||
PCNT { _instance }
|
||||
}
|
||||
|
||||
/// Return a unit
|
||||
pub fn get_unit(&self, number: unit::Number) -> Unit {
|
||||
Unit::new(number)
|
||||
}
|
||||
}
|
||||
@ -1,392 +0,0 @@
|
||||
use critical_section::CriticalSection;
|
||||
|
||||
use super::channel;
|
||||
|
||||
/// Unit number
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Number {
|
||||
Unit0,
|
||||
Unit1,
|
||||
Unit2,
|
||||
Unit3,
|
||||
#[cfg(esp32)]
|
||||
Unit4,
|
||||
#[cfg(esp32)]
|
||||
Unit5,
|
||||
#[cfg(esp32)]
|
||||
Unit6,
|
||||
#[cfg(esp32)]
|
||||
Unit7,
|
||||
}
|
||||
|
||||
/// Unit errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Invalid filter threshold value
|
||||
InvalidFilterThresh,
|
||||
/// Invalid low limit - must be < 0
|
||||
InvalidLowLimit,
|
||||
/// Invalid high limit - must be > 0
|
||||
InvalidHighLimit,
|
||||
}
|
||||
|
||||
/// the current status of the counter.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub enum ZeroMode {
|
||||
/// pulse counter decreases from positive to 0.
|
||||
#[default]
|
||||
PosZero = 0,
|
||||
/// pulse counter increases from negative to 0
|
||||
NegZero = 1,
|
||||
/// pulse counter is negative (not implemented?)
|
||||
Negitive = 2,
|
||||
/// pulse counter is positive (not implemented?)
|
||||
Positive = 3,
|
||||
}
|
||||
|
||||
impl From<u8> for ZeroMode {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0 => Self::PosZero,
|
||||
1 => Self::NegZero,
|
||||
2 => Self::Negitive,
|
||||
3 => Self::Positive,
|
||||
_ => unreachable!(), // TODO: is this good enoough? should we use some default?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Events {
|
||||
pub low_limit: bool,
|
||||
pub high_limit: bool,
|
||||
pub thresh0: bool,
|
||||
pub thresh1: bool,
|
||||
pub zero: bool,
|
||||
}
|
||||
|
||||
/// Unit configuration
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Config {
|
||||
pub low_limit: i16,
|
||||
pub high_limit: i16,
|
||||
pub thresh0: i16,
|
||||
pub thresh1: i16,
|
||||
pub filter: Option<u16>,
|
||||
}
|
||||
|
||||
pub struct Unit {
|
||||
number: Number,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
/// return a new Unit
|
||||
pub(super) fn new(number: Number) -> Self {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match number {
|
||||
Number::Unit0 => &pcnt.u0_conf0,
|
||||
Number::Unit1 => &pcnt.u1_conf0,
|
||||
Number::Unit2 => &pcnt.u2_conf0,
|
||||
Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
// disable filter and all events
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.filter_en()
|
||||
.clear_bit()
|
||||
.filter_thres()
|
||||
.bits(0)
|
||||
.thr_l_lim_en()
|
||||
.clear_bit()
|
||||
.thr_h_lim_en()
|
||||
.clear_bit()
|
||||
.thr_thres0_en()
|
||||
.clear_bit()
|
||||
.thr_thres1_en()
|
||||
.clear_bit()
|
||||
.thr_zero_en()
|
||||
.clear_bit()
|
||||
});
|
||||
Self { number }
|
||||
}
|
||||
|
||||
pub fn configure(&mut self, config: Config) -> Result<(), Error> {
|
||||
// low limit must be >= or the limit is -32768 and when thats
|
||||
// hit the event status claims it was the high limit.
|
||||
// tested on an esp32s3
|
||||
if config.low_limit >= 0 {
|
||||
return Err(Error::InvalidLowLimit);
|
||||
}
|
||||
if config.high_limit <= 0 {
|
||||
return Err(Error::InvalidHighLimit);
|
||||
}
|
||||
let (filter_en, filter) = match config.filter {
|
||||
Some(filter) => (true, filter),
|
||||
None => (false, 0),
|
||||
};
|
||||
// filter must be less than 1024
|
||||
if filter > 1023 {
|
||||
return Err(Error::InvalidFilterThresh);
|
||||
}
|
||||
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let (conf0, conf1, conf2) = match self.number {
|
||||
Number::Unit0 => (&pcnt.u0_conf0, &pcnt.u0_conf1, &pcnt.u0_conf2),
|
||||
Number::Unit1 => (&pcnt.u1_conf0, &pcnt.u1_conf1, &pcnt.u1_conf2),
|
||||
Number::Unit2 => (&pcnt.u2_conf0, &pcnt.u2_conf1, &pcnt.u2_conf2),
|
||||
Number::Unit3 => (&pcnt.u3_conf0, &pcnt.u3_conf1, &pcnt.u3_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => (&pcnt.u4_conf0, &pcnt.u4_conf1, &pcnt.u4_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => (&pcnt.u5_conf0, &pcnt.u5_conf1, &pcnt.u5_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => (&pcnt.u6_conf0, &pcnt.u6_conf1, &pcnt.u6_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => (&pcnt.u7_conf0, &pcnt.u7_conf1, &pcnt.u7_conf2),
|
||||
};
|
||||
conf2.write(|w| unsafe {
|
||||
w.cnt_l_lim()
|
||||
.bits(config.low_limit as u16)
|
||||
.cnt_h_lim()
|
||||
.bits(config.high_limit as u16)
|
||||
});
|
||||
conf1.write(|w| unsafe {
|
||||
w.cnt_thres0()
|
||||
.bits(config.thresh0 as u16)
|
||||
.cnt_thres1()
|
||||
.bits(config.thresh1 as u16)
|
||||
});
|
||||
conf0.modify(|_, w| unsafe { w.filter_thres().bits(filter).filter_en().bit(filter_en) });
|
||||
self.pause();
|
||||
self.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_channel(&self, number: channel::Number) -> super::channel::Channel {
|
||||
super::channel::Channel::new(self.number, number)
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().set_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().set_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().set_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().set_bit()),
|
||||
}
|
||||
// TODO: does this need a delay? (liebman / Jan 2 2023)
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().clear_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().clear_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().clear_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().clear_bit()),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Pause the counter
|
||||
pub fn pause(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().set_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().set_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().set_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().set_bit()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Resume the counter
|
||||
pub fn resume(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().clear_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().clear_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().clear_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().clear_bit()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Enable which events generate interrupts on this unit.
|
||||
pub fn events(&self, events: Events) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match self.number {
|
||||
Number::Unit0 => &pcnt.u0_conf0,
|
||||
Number::Unit1 => &pcnt.u1_conf0,
|
||||
Number::Unit2 => &pcnt.u2_conf0,
|
||||
Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
conf0.modify(|_, w| {
|
||||
w.thr_l_lim_en()
|
||||
.bit(events.low_limit)
|
||||
.thr_h_lim_en()
|
||||
.bit(events.high_limit)
|
||||
.thr_thres0_en()
|
||||
.bit(events.thresh0)
|
||||
.thr_thres1_en()
|
||||
.bit(events.thresh1)
|
||||
.thr_zero_en()
|
||||
.bit(events.zero)
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the latest events for this unit.
|
||||
pub fn get_events(&self) -> Events {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let status = pcnt.u_status[self.number as usize].read();
|
||||
|
||||
Events {
|
||||
low_limit: status.l_lim().bit(),
|
||||
high_limit: status.h_lim().bit(),
|
||||
thresh0: status.thres0().bit(),
|
||||
thresh1: status.thres1().bit(),
|
||||
zero: status.zero().bit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the mode of the last zero crossing
|
||||
pub fn get_zero_mode(&self) -> ZeroMode {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
pcnt.u_status[self.number as usize]
|
||||
.read()
|
||||
.zero_mode()
|
||||
.bits()
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Enable interrupts for this unit.
|
||||
pub fn listen(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_ena.modify(|_, w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Disable interrupts for this unit.
|
||||
pub fn unlisten(&self, _cs: CriticalSection) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_ena.write(|w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().clear_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().clear_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().clear_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().clear_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns true if an interrupt is active for this unit.
|
||||
pub fn interrupt_set(&self) -> bool {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.int_st.read().cnt_thr_event_u0().bit(),
|
||||
Number::Unit1 => pcnt.int_st.read().cnt_thr_event_u1().bit(),
|
||||
Number::Unit2 => pcnt.int_st.read().cnt_thr_event_u2().bit(),
|
||||
Number::Unit3 => pcnt.int_st.read().cnt_thr_event_u3().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.int_st.read().cnt_thr_event_u4().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.int_st.read().cnt_thr_event_u5().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.int_st.read().cnt_thr_event_u6().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.int_st.read().cnt_thr_event_u7().bit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the interrupt bit for this unit.
|
||||
pub fn reset_interrupt(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_clr.write(|w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the current counter value.
|
||||
pub fn get_value(&self) -> i16 {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
pcnt.u_cnt[self.number as usize].read().cnt().bits() as i16
|
||||
}
|
||||
}
|
||||
@ -1,347 +0,0 @@
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
/// An exclusive reference to a peripheral.
|
||||
///
|
||||
/// This is functionally the same as a `&'a mut T`. The reason for having a
|
||||
/// dedicated struct is memory efficiency:
|
||||
///
|
||||
/// Peripheral singletons are typically either zero-sized (for concrete
|
||||
/// peripehrals like `PA9` or `Spi4`) or very small (for example `AnyPin` which
|
||||
/// is 1 byte). However `&mut T` is always 4 bytes for 32-bit targets, even if T
|
||||
/// is zero-sized. PeripheralRef stores a copy of `T` instead, so it's the same
|
||||
/// size.
|
||||
///
|
||||
/// but it is the size of `T` not the size
|
||||
/// of a pointer. This is useful if T is a zero sized type.
|
||||
pub struct PeripheralRef<'a, T> {
|
||||
inner: T,
|
||||
_lifetime: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T> PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `reborrow()` instead. It returns a
|
||||
/// `PeripheralRef` that borrows `self`, which allows the borrow checker
|
||||
/// to enforce this at compile time.
|
||||
pub unsafe fn clone_unchecked(&mut self) -> PeripheralRef<'a, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
PeripheralRef::new(self.inner.clone_unchecked())
|
||||
}
|
||||
|
||||
/// Reborrow into a "child" PeripheralRef.
|
||||
///
|
||||
/// `self` will stay borrowed until the child PeripheralRef is dropped.
|
||||
pub fn reborrow(&mut self) -> PeripheralRef<'_, T>
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
// safety: we're returning the clone inside a new PeripheralRef that borrows
|
||||
// self, so user code can't use both at the same time.
|
||||
PeripheralRef::new(unsafe { self.inner.clone_unchecked() })
|
||||
}
|
||||
|
||||
/// Map the inner peripheral using `Into`.
|
||||
///
|
||||
/// This converts from `PeripheralRef<'a, T>` to `PeripheralRef<'a, U>`,
|
||||
/// using an `Into` impl to convert from `T` to `U`.
|
||||
///
|
||||
/// For example, this can be useful to degrade GPIO pins: converting from
|
||||
/// PeripheralRef<'a, PB11>` to `PeripheralRef<'a, AnyPin>`.
|
||||
#[inline]
|
||||
pub fn map_into<U>(self) -> PeripheralRef<'a, U>
|
||||
where
|
||||
T: Into<U>,
|
||||
{
|
||||
PeripheralRef {
|
||||
inner: self.inner.into(),
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for PeripheralRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for any type that can be used as a peripheral of type `P`.
|
||||
///
|
||||
/// This is used in driver constructors, to allow passing either owned
|
||||
/// peripherals (e.g. `TWISPI0`), or borrowed peripherals (e.g. `&mut TWISPI0`).
|
||||
///
|
||||
/// For example, if you have a driver with a constructor like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl<'d, T: Instance> Twim<'d, T> {
|
||||
/// pub fn new(
|
||||
/// twim: impl Peripheral<P = T> + 'd,
|
||||
/// irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
/// sda: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// scl: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
/// config: Config,
|
||||
/// ) -> Self { .. }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You may call it with owned peripherals, which yields an instance that can
|
||||
/// live forever (`'static`):
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'static, ...> = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// Or you may call it with borrowed peripherals, which yields an instance that
|
||||
/// can only live for as long as the borrows last:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let mut twi: Twim<'_, ...> = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config);
|
||||
/// ```
|
||||
///
|
||||
/// # Implementation details, for HAL authors
|
||||
///
|
||||
/// When writing a HAL, the intended way to use this trait is to take `impl
|
||||
/// Peripheral<P = ..>` in the HAL's public API (such as driver constructors),
|
||||
/// calling `.into_ref()` to obtain a `PeripheralRef`, and storing that in the
|
||||
/// driver struct.
|
||||
///
|
||||
/// `.into_ref()` on an owned `T` yields a `PeripheralRef<'static, T>`.
|
||||
/// `.into_ref()` on an `&'a mut T` yields a `PeripheralRef<'a, T>`.
|
||||
pub trait Peripheral: Sized + sealed::Sealed {
|
||||
/// Peripheral singleton type
|
||||
type P;
|
||||
|
||||
/// Unsafely clone (duplicate) a peripheral singleton.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This returns an owned clone of the peripheral. You must manually ensure
|
||||
/// only one copy of the peripheral is in use at a time. For example, don't
|
||||
/// create two SPI drivers on `SPI1`, because they will "fight" each other.
|
||||
///
|
||||
/// You should strongly prefer using `into_ref()` instead. It returns a
|
||||
/// `PeripheralRef`, which allows the borrow checker to enforce this at
|
||||
/// compile time.
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P;
|
||||
|
||||
/// Convert a value into a `PeripheralRef`.
|
||||
///
|
||||
/// When called on an owned `T`, yields a `PeripheralRef<'static, T>`.
|
||||
/// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`.
|
||||
#[inline]
|
||||
fn into_ref<'a>(mut self) -> PeripheralRef<'a, Self::P>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
PeripheralRef::new(unsafe { self.clone_unchecked() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Peripheral for &mut T
|
||||
where
|
||||
T: Peripheral<P = T>,
|
||||
{
|
||||
type P = T;
|
||||
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
T::clone_unchecked(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> sealed::Sealed for &mut T where T: sealed::Sealed {}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
mod peripheral_macros {
|
||||
#[macro_export]
|
||||
macro_rules! peripherals {
|
||||
($($(#[$cfg:meta])? $name:ident => $from_pac:tt),*$(,)?) => {
|
||||
|
||||
/// Contains the generated peripherals which implement [`Peripheral`]
|
||||
mod peripherals {
|
||||
pub use super::pac::*;
|
||||
$(
|
||||
crate::create_peripheral!($(#[$cfg])? $name => $from_pac);
|
||||
)*
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct Peripherals {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
pub $name: peripherals::$name,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Peripherals {
|
||||
/// Returns all the peripherals *once*
|
||||
#[inline]
|
||||
pub fn take() -> Self {
|
||||
|
||||
#[no_mangle]
|
||||
static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false;
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
if _ESP_HAL_DEVICE_PERIPHERALS {
|
||||
panic!("init called more than once!")
|
||||
}
|
||||
_ESP_HAL_DEVICE_PERIPHERALS = true;
|
||||
Self::steal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Peripherals {
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name: peripherals::$name::steal(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expose the new structs
|
||||
$(
|
||||
pub use peripherals::$name;
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! into_ref {
|
||||
($($name:ident),*) => {
|
||||
$(
|
||||
#[allow(unused_mut)]
|
||||
let mut $name = $name.into_ref();
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_peripheral {
|
||||
($(#[$cfg:meta])? $name:ident => true) => {
|
||||
$(#[$cfg])?
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name { _inner: () }
|
||||
|
||||
$(#[$cfg])?
|
||||
impl $name {
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self { _inner: () }
|
||||
}
|
||||
|
||||
#[doc = r"Pointer to the register block"]
|
||||
pub const PTR: *const <super::pac::$name as core::ops::Deref>::Target = super::pac::$name::PTR;
|
||||
|
||||
#[doc = r"Return the pointer to the register block"]
|
||||
#[inline(always)]
|
||||
pub const fn ptr() -> *const <super::pac::$name as core::ops::Deref>::Target {
|
||||
super::pac::$name::PTR
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Deref for $name {
|
||||
type Target = <super::pac::$name as core::ops::Deref>::Target;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*Self::PTR }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::DerefMut for $name {
|
||||
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *(Self::PTR as *mut _) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for $name {
|
||||
type P = $name;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for $name {}
|
||||
};
|
||||
($(#[$cfg:meta])? $name:ident => false) => {
|
||||
$(#[$cfg])?
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name { _inner: () }
|
||||
|
||||
$(#[$cfg])?
|
||||
impl $name {
|
||||
/// Unsafely create an instance of this peripheral out of thin air.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// You must ensure that you're only using one instance of this type at a time.
|
||||
#[inline]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self { _inner: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for $name {
|
||||
type P = $name;
|
||||
|
||||
#[inline]
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for $name {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
//! The prelude
|
||||
//!
|
||||
//! Re-exports all traits required for interacting with the various peripheral
|
||||
//! drivers implemented in this crate.
|
||||
|
||||
pub use embedded_dma::{
|
||||
ReadBuffer as _embedded_dma_ReadBuffer,
|
||||
ReadTarget as _embedded_dma_ReadTarget,
|
||||
Word as _embedded_dma_Word,
|
||||
WriteBuffer as _embedded_dma_WriteBuffer,
|
||||
WriteTarget as _embedded_dma_WriteTarget,
|
||||
};
|
||||
pub use embedded_hal::{
|
||||
digital::v2::{
|
||||
InputPin as _embedded_hal_digital_v2_InputPin,
|
||||
OutputPin as _embedded_hal_digital_v2_OutputPin,
|
||||
StatefulOutputPin as _embedded_hal_digital_v2_StatefulOutputPin,
|
||||
ToggleableOutputPin as _embedded_hal_digital_v2_ToggleableOutputPin,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
#[cfg(feature = "async")]
|
||||
pub use embedded_hal_async::{
|
||||
delay::DelayUs as _embedded_hal_async_delay_DelayUs,
|
||||
digital::Wait as _embedded_hal_async_digital_Wait,
|
||||
i2c::I2c as _embedded_hal_async_i2c_I2c,
|
||||
spi::SpiBus as _embedded_hal_spi_SpiBus,
|
||||
spi::SpiBusFlush as _embedded_hal_spi_SpiBusFlush,
|
||||
spi::SpiBusRead as _embedded_hal_spi_SpiBusRead,
|
||||
spi::SpiBusWrite as _embedded_hal_spi_SpiBusWrite,
|
||||
spi::SpiDevice as _embedded_hal_spi_SpiDevice,
|
||||
};
|
||||
pub use fugit::{
|
||||
ExtU32 as _fugit_ExtU32,
|
||||
ExtU64 as _fugit_ExtU64,
|
||||
RateExtU32 as _fugit_RateExtU32,
|
||||
RateExtU64 as _fugit_RateExtU64,
|
||||
};
|
||||
pub use nb;
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
|
||||
pub use crate::analog::SarAdcExt as _esp_hal_analog_SarAdcExt;
|
||||
#[cfg(sens)]
|
||||
pub use crate::analog::SensExt as _esp_hal_analog_SensExt;
|
||||
#[cfg(any(gdma, pdma))]
|
||||
pub use crate::dma::{
|
||||
DmaTransfer as _esp_hal_dma_DmaTransfer,
|
||||
DmaTransferRxTx as _esp_hal_dma_DmaTransferRxTx,
|
||||
};
|
||||
#[cfg(gpio)]
|
||||
pub use crate::gpio::{
|
||||
InputPin as _esp_hal_gpio_InputPin,
|
||||
OutputPin as _esp_hal_gpio_OutputPin,
|
||||
Pin as _esp_hal_gpio_Pin,
|
||||
};
|
||||
#[cfg(any(i2c0, i2c1))]
|
||||
pub use crate::i2c::Instance as _esp_hal_i2c_Instance;
|
||||
#[cfg(ledc)]
|
||||
pub use crate::ledc::{
|
||||
channel::{
|
||||
ChannelHW as _esp_hal_ledc_channel_ChannelHW,
|
||||
ChannelIFace as _esp_hal_ledc_channel_ChannelIFace,
|
||||
},
|
||||
timer::{TimerHW as _esp_hal_ledc_timer_TimerHW, TimerIFace as _esp_hal_ledc_timer_TimerIFace},
|
||||
};
|
||||
#[cfg(rmt)]
|
||||
pub use crate::pulse_control::{
|
||||
ConfiguredChannel as _esp_hal_pulse_control_ConfiguredChannel,
|
||||
OutputChannel as _esp_hal_pulse_control_OutputChannel,
|
||||
};
|
||||
#[cfg(radio)]
|
||||
pub use crate::radio::RadioExt as _esp_hal_RadioExt;
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
pub use crate::spi::dma::WithDmaSpi3 as _esp_hal_spi_dma_WithDmaSpi3;
|
||||
#[cfg(any(spi0, spi1, spi2, spi3))]
|
||||
pub use crate::spi::{
|
||||
dma::WithDmaSpi2 as _esp_hal_spi_dma_WithDmaSpi2,
|
||||
Instance as _esp_hal_spi_Instance,
|
||||
InstanceDma as _esp_hal_spi_InstanceDma,
|
||||
};
|
||||
#[cfg(any(dport, pcr, system))]
|
||||
pub use crate::system::SystemExt as _esp_hal_system_SystemExt;
|
||||
#[cfg(any(timg0, timg1))]
|
||||
pub use crate::timer::{
|
||||
Instance as _esp_hal_timer_Instance,
|
||||
TimerGroupInstance as _esp_hal_timer_TimerGroupInstance,
|
||||
};
|
||||
#[cfg(any(uart0, uart1, uart2))]
|
||||
pub use crate::uart::{Instance as _esp_hal_uart_Instance, UartPins as _esp_hal_uart_UartPins};
|
||||
pub use crate::{clock::Clock as _esp_hal_clock_Clock, entry, macros::*};
|
||||
|
||||
/// All traits required for using the 1.0.0-alpha.x release of embedded-hal
|
||||
#[cfg(feature = "eh1")]
|
||||
pub mod eh1 {
|
||||
#[cfg(any(twai0, twai1))]
|
||||
pub use embedded_can::{
|
||||
blocking::Can as _embedded_can_blocking_Can,
|
||||
nb::Can as _embedded_can_nb_Can,
|
||||
Error as _embedded_can_Error,
|
||||
Frame as _embedded_can_Frame,
|
||||
};
|
||||
pub use embedded_hal_1::{
|
||||
delay::DelayUs as _embedded_hal_delay_blocking_DelayUs,
|
||||
digital::{
|
||||
InputPin as _embedded_hal_digital_blocking_InputPin,
|
||||
OutputPin as _embedded_hal_digital_blocking_OutputPin,
|
||||
StatefulOutputPin as _embedded_hal_digital_blocking_StatefulOutputPin,
|
||||
ToggleableOutputPin as _embedded_hal_digital_blocking_ToggleableOutputPin,
|
||||
},
|
||||
i2c::I2c as _embedded_hal_i2c_blocking_I2c,
|
||||
spi::{
|
||||
SpiBus as _embedded_hal_spi_blocking_SpiBus,
|
||||
SpiBusFlush as _embedded_hal_spi_blocking_SpiBusFlush,
|
||||
SpiBusRead as _embedded_hal_spi_blocking_SpiBusRead,
|
||||
SpiBusWrite as _embedded_hal_spi_blocking_SpiBusWrite,
|
||||
},
|
||||
};
|
||||
pub use embedded_hal_nb::{
|
||||
serial::{Read as _embedded_hal_nb_serial_Read, Write as _embedded_hal_nb_serial_Write},
|
||||
spi::FullDuplex as _embedded_hal_nb_spi_FullDuplex,
|
||||
};
|
||||
|
||||
pub use super::*;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,102 +0,0 @@
|
||||
pub trait RadioExt {
|
||||
type Components;
|
||||
|
||||
fn split(self) -> Self::Components;
|
||||
}
|
||||
|
||||
/// WiFi radio
|
||||
pub struct Wifi {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// Bluetooth radio
|
||||
pub struct Bluetooth {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
/// IEEE 802.15.4 Low rate wireless personal area radio
|
||||
pub struct LowRate {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Wifi {
|
||||
pub const unsafe fn steal() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for Wifi {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for Wifi {}
|
||||
|
||||
impl Bluetooth {
|
||||
pub const unsafe fn steal() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for Bluetooth {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for Bluetooth {}
|
||||
|
||||
impl LowRate {
|
||||
pub const unsafe fn steal() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::Peripheral for LowRate {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&mut self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::peripheral::sealed::Sealed for LowRate {}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32c2, esp32c3, esp32s3))] {
|
||||
impl RadioExt for crate::peripherals::RADIO {
|
||||
type Components = (Wifi, Bluetooth);
|
||||
|
||||
fn split(self) -> Self::Components {
|
||||
unsafe {
|
||||
(Wifi::steal(), Bluetooth::steal())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if #[cfg(esp32c6)] {
|
||||
impl RadioExt for crate::peripherals::RADIO {
|
||||
type Components = (Wifi, Bluetooth, LowRate);
|
||||
|
||||
fn split(self) -> Self::Components {
|
||||
unsafe {
|
||||
(Wifi::steal(), Bluetooth::steal(), LowRate::steal())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if #[cfg(esp32s2)] {
|
||||
impl RadioExt for crate::peripherals::RADIO {
|
||||
type Components = Wifi;
|
||||
|
||||
fn split(self) -> Self::Components {
|
||||
unsafe {
|
||||
Wifi::steal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
//! Random number generator driver
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
||||
use embedded_hal::blocking::rng::Read;
|
||||
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals::RNG,
|
||||
};
|
||||
|
||||
/// Random Number Generator
|
||||
///
|
||||
/// It should be noted that there are certain pre-conditions which must be met
|
||||
/// in order for the RNG to produce *true* random numbers. The hardware RNG
|
||||
/// produces true random numbers under any of the following conditions:
|
||||
///
|
||||
/// - RF subsystem is enabled (i.e. Wi-Fi or Bluetooth are enabled).
|
||||
/// - An internal entropy source has been enabled by calling
|
||||
/// `bootloader_random_enable()` and not yet disabled by calling
|
||||
/// `bootloader_random_disable()`.
|
||||
/// - While the ESP-IDF Second stage bootloader is running. This is because the
|
||||
/// default ESP-IDF bootloader implementation calls
|
||||
/// `bootloader_random_enable()` when the bootloader starts, and
|
||||
/// `bootloader_random_disable()` before executing the app.
|
||||
///
|
||||
/// When any of these conditions are true, samples of physical noise are
|
||||
/// continuously mixed into the internal hardware RNG state to provide entropy.
|
||||
/// If none of the above conditions are true, the output of the RNG should be
|
||||
/// considered pseudo-random only.
|
||||
///
|
||||
/// For more information, please refer to the ESP-IDF documentation:
|
||||
/// <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html>
|
||||
pub struct Rng<'d> {
|
||||
rng: PeripheralRef<'d, RNG>,
|
||||
}
|
||||
|
||||
impl<'d> Rng<'d> {
|
||||
/// Create a new random number generator instance
|
||||
pub fn new(rng: impl Peripheral<P = RNG> + 'd) -> Self {
|
||||
crate::into_ref!(rng);
|
||||
|
||||
Self { rng }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Reads currently available `u32` integer from `RNG`
|
||||
pub fn random(&mut self) -> u32 {
|
||||
self.rng.data.read().bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Rng<'_> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
for chunk in buffer.chunks_mut(4) {
|
||||
let bytes = self.random().to_le_bytes();
|
||||
chunk.copy_from_slice(&bytes[..chunk.len()]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
pub use paste::paste;
|
||||
|
||||
pub mod crc;
|
||||
|
||||
#[allow(unused)]
|
||||
extern "C" {
|
||||
pub(crate) fn rom_i2c_writeReg(block: u32, block_hostid: u32, reg_add: u32, indata: u32);
|
||||
|
||||
pub(crate) fn rom_i2c_writeReg_Mask(
|
||||
block: u32,
|
||||
block_hostid: u32,
|
||||
reg_add: u32,
|
||||
reg_add_msb: u32,
|
||||
reg_add_lsb: u32,
|
||||
indata: u32,
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! regi2c_write {
|
||||
( $block: ident, $reg_add: ident, $indata: expr ) => {
|
||||
paste! {
|
||||
rom_i2c_writeReg($block,
|
||||
[<$block _HOSTID>],
|
||||
$reg_add,
|
||||
$indata
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! regi2c_write_mask {
|
||||
( $block: ident, $reg_add: ident, $indata: expr ) => {
|
||||
paste! {
|
||||
rom_i2c_writeReg_Mask($block,
|
||||
[<$block _HOSTID>],
|
||||
$reg_add,
|
||||
[<$reg_add _MSB>],
|
||||
[<$reg_add _LSB>],
|
||||
$indata
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,211 +0,0 @@
|
||||
use core::{
|
||||
convert::Infallible,
|
||||
marker::PhantomData,
|
||||
ptr::{copy_nonoverlapping, write_bytes},
|
||||
};
|
||||
|
||||
use crate::rsa::{
|
||||
implement_op,
|
||||
Multi,
|
||||
Rsa,
|
||||
RsaMode,
|
||||
RsaModularExponentiation,
|
||||
RsaModularMultiplication,
|
||||
RsaMultiplication,
|
||||
};
|
||||
|
||||
impl<'d> Rsa<'d> {
|
||||
/// After the RSA Accelerator is released from reset, the memory blocks
|
||||
/// needs to be initialized, only after that peripheral should be used.
|
||||
/// This function would return without an error if the memory is initialized
|
||||
pub fn ready(&mut self) -> nb::Result<(), Infallible> {
|
||||
if self.rsa.clean.read().clean().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn write_multi_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.rsa.mult_mode, mode as u32);
|
||||
}
|
||||
|
||||
pub(super) fn write_modexp_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.rsa.modexp_mode, mode);
|
||||
}
|
||||
|
||||
pub(super) fn write_modexp_start(&mut self) {
|
||||
self.rsa.modexp_start.write(|w| w.modexp_start().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn write_multi_start(&mut self) {
|
||||
self.rsa.mult_start.write(|w| w.mult_start().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn clear_interrupt(&mut self) {
|
||||
self.rsa.interrupt.write(|w| w.interrupt().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn is_idle(&mut self) -> bool {
|
||||
self.rsa.interrupt.read().bits() == 1
|
||||
}
|
||||
|
||||
unsafe fn write_multi_operand_a<const N: usize>(&mut self, operand_a: &[u8; N]) {
|
||||
copy_nonoverlapping(
|
||||
operand_a.as_ptr(),
|
||||
self.rsa.x_mem.as_mut_ptr() as *mut u8,
|
||||
N,
|
||||
);
|
||||
write_bytes(self.rsa.x_mem.as_mut_ptr().add(N), 0, N);
|
||||
}
|
||||
|
||||
unsafe fn write_multi_operand_b<const N: usize>(&mut self, operand_b: &[u8; N]) {
|
||||
write_bytes(self.rsa.z_mem.as_mut_ptr(), 0, N);
|
||||
copy_nonoverlapping(
|
||||
operand_b.as_ptr(),
|
||||
self.rsa.z_mem.as_mut_ptr().add(N) as *mut u8,
|
||||
N,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod operand_sizes {
|
||||
//! Marker types for the operand sizes
|
||||
use paste::paste;
|
||||
|
||||
use super::{implement_op, Multi, RsaMode};
|
||||
|
||||
implement_op!(
|
||||
(512, multi),
|
||||
(1024, multi),
|
||||
(1536, multi),
|
||||
(2048, multi),
|
||||
(2560),
|
||||
(3072),
|
||||
(3584),
|
||||
(4096)
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaMultiplication`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 24.3.2 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>
|
||||
pub fn new(rsa: &'a mut Rsa<'d>, modulus: &T::InputType, m_prime: u32) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_modulus(modulus);
|
||||
}
|
||||
rsa.write_mprime(m_prime);
|
||||
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_multi_mode((N / 64 - 1) as u32)
|
||||
}
|
||||
|
||||
/// Starts the first step of modular multiplication operation. `r` could be
|
||||
/// calculated using `2 ^ ( bitlength * 2 ) mod modulus`,
|
||||
/// for more information check 24.3.2 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>
|
||||
pub fn start_step1(&mut self, operand_a: &T::InputType, r: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_operand_a(operand_a);
|
||||
self.rsa.write_r(r);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
/// Starts the second step of modular multiplication operation.
|
||||
/// This is a non blocking function that returns without an error if
|
||||
/// operation is completed successfully. `start_step1` must be called
|
||||
/// before calling this function.
|
||||
pub fn start_step2(&mut self, operand_b: &T::InputType) -> nb::Result<(), Infallible> {
|
||||
if !self.rsa.is_idle() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
self.rsa.clear_interrupt();
|
||||
unsafe {
|
||||
self.rsa.write_operand_a(operand_b);
|
||||
}
|
||||
self.set_start();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_start(&mut self) {
|
||||
self.rsa.write_multi_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaModularExponentiation`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 24.3.2 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>
|
||||
pub fn new(
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
exponent: &T::InputType,
|
||||
modulus: &T::InputType,
|
||||
m_prime: u32,
|
||||
) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_operand_b(exponent);
|
||||
rsa.write_modulus(modulus);
|
||||
}
|
||||
rsa.write_mprime(m_prime);
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_modexp_mode((N / 64 - 1) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_modexp_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaMultiplication`.
|
||||
pub fn new(rsa: &'a mut Rsa<'d>) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the multiplication operation.
|
||||
pub fn start_multiplication(&mut self, operand_a: &T::InputType, operand_b: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_multi_operand_a(operand_a);
|
||||
self.rsa.write_multi_operand_b(operand_b);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_multi_mode((N / 32 - 1 + 8) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_multi_start();
|
||||
}
|
||||
}
|
||||
@ -1,347 +0,0 @@
|
||||
use core::{convert::Infallible, marker::PhantomData, ptr::copy_nonoverlapping};
|
||||
|
||||
use crate::rsa::{
|
||||
implement_op,
|
||||
Multi,
|
||||
Rsa,
|
||||
RsaMode,
|
||||
RsaModularExponentiation,
|
||||
RsaModularMultiplication,
|
||||
RsaMultiplication,
|
||||
};
|
||||
|
||||
impl<'d> Rsa<'d> {
|
||||
/// After the RSA Accelerator is released from reset, the memory blocks
|
||||
/// needs to be initialized, only after that peripheral should be used.
|
||||
/// This function would return without an error if the memory is initialized
|
||||
pub fn ready(&mut self) -> nb::Result<(), Infallible> {
|
||||
if self.rsa.query_clean.read().query_clean().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enables/disables rsa interrupt, when enabled rsa perpheral would
|
||||
/// generate an interrupt when a operation is finished.
|
||||
pub fn enable_disable_interrupt(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self.rsa.int_ena.write(|w| w.int_ena().set_bit()),
|
||||
false => self.rsa.int_ena.write(|w| w.int_ena().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.rsa.mode, mode as u32);
|
||||
}
|
||||
|
||||
/// Enables/disables search acceleration, when enabled it would increases
|
||||
/// the performance of modular exponentiation by discarding the
|
||||
/// exponent's bits before the most significant set bit. Note: this might
|
||||
/// affect the security, for more info refer 18.3.4 of <https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf>
|
||||
pub fn enable_disable_search_acceleration(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self
|
||||
.rsa
|
||||
.search_enable
|
||||
.write(|w| w.search_enable().set_bit()),
|
||||
false => self
|
||||
.rsa
|
||||
.search_enable
|
||||
.write(|w| w.search_enable().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_search_enabled(&mut self) -> bool {
|
||||
self.rsa.search_enable.read().search_enable().bit_is_set()
|
||||
}
|
||||
|
||||
pub(super) fn write_search_position(&mut self, search_position: u32) {
|
||||
Self::write_to_register(&mut self.rsa.search_pos, search_position);
|
||||
}
|
||||
|
||||
/// Enables/disables constant time acceleration, when enabled it would
|
||||
/// increases the performance of modular exponentiation by simplifying
|
||||
/// the calculation concerning the 0 bits of the exponent i.e. lesser the
|
||||
/// hamming weight, greater the performance. Note : this might affect
|
||||
/// the security, for more info refer 18.3.4 of <https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf>
|
||||
pub fn enable_disable_constant_time_acceleration(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self
|
||||
.rsa
|
||||
.constant_time
|
||||
.write(|w| w.constant_time().clear_bit()),
|
||||
false => self
|
||||
.rsa
|
||||
.constant_time
|
||||
.write(|w| w.constant_time().set_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_modexp_start(&mut self) {
|
||||
self.rsa
|
||||
.set_start_modexp
|
||||
.write(|w| w.set_start_modexp().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn write_multi_start(&mut self) {
|
||||
self.rsa
|
||||
.set_start_mult
|
||||
.write(|w| w.set_start_mult().set_bit());
|
||||
}
|
||||
|
||||
fn write_modmulti_start(&mut self) {
|
||||
self.rsa
|
||||
.set_start_modmult
|
||||
.write(|w| w.set_start_modmult().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn clear_interrupt(&mut self) {
|
||||
self.rsa.int_clr.write(|w| w.clear_interrupt().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn is_idle(&mut self) -> bool {
|
||||
self.rsa.query_idle.read().query_idle().bit_is_set()
|
||||
}
|
||||
|
||||
unsafe fn write_multi_operand_b<const N: usize>(&mut self, operand_b: &[u8; N]) {
|
||||
copy_nonoverlapping(
|
||||
operand_b.as_ptr(),
|
||||
self.rsa.z_mem.as_mut_ptr().add(N) as *mut u8,
|
||||
N,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod operand_sizes {
|
||||
//! Marker types for the operand sizes
|
||||
use paste::paste;
|
||||
|
||||
use super::{implement_op, Multi, RsaMode};
|
||||
|
||||
implement_op!(
|
||||
(32, multi),
|
||||
(64, multi),
|
||||
(96, multi),
|
||||
(128, multi),
|
||||
(160, multi),
|
||||
(192, multi),
|
||||
(224, multi),
|
||||
(256, multi),
|
||||
(288, multi),
|
||||
(320, multi),
|
||||
(352, multi),
|
||||
(384, multi),
|
||||
(416, multi),
|
||||
(448, multi),
|
||||
(480, multi),
|
||||
(512, multi),
|
||||
(544, multi),
|
||||
(576, multi),
|
||||
(608, multi),
|
||||
(640, multi),
|
||||
(672, multi),
|
||||
(704, multi),
|
||||
(736, multi),
|
||||
(768, multi),
|
||||
(800, multi),
|
||||
(832, multi),
|
||||
(864, multi),
|
||||
(896, multi),
|
||||
(928, multi),
|
||||
(960, multi),
|
||||
(992, multi),
|
||||
(1024, multi),
|
||||
(1056, multi),
|
||||
(1088, multi),
|
||||
(1120, multi),
|
||||
(1152, multi),
|
||||
(1184, multi),
|
||||
(1216, multi),
|
||||
(1248, multi),
|
||||
(1280, multi),
|
||||
(1312, multi),
|
||||
(1344, multi),
|
||||
(1376, multi),
|
||||
(1408, multi),
|
||||
(1440, multi),
|
||||
(1472, multi),
|
||||
(1504, multi),
|
||||
(1536, multi),
|
||||
(1568),
|
||||
(1600),
|
||||
(1632),
|
||||
(1664),
|
||||
(1696),
|
||||
(1728),
|
||||
(1760),
|
||||
(1792),
|
||||
(1824),
|
||||
(1856),
|
||||
(1888),
|
||||
(1920),
|
||||
(1952),
|
||||
(1984),
|
||||
(2016),
|
||||
(2048),
|
||||
(2080),
|
||||
(2112),
|
||||
(2144),
|
||||
(2176),
|
||||
(2208),
|
||||
(2240),
|
||||
(2272),
|
||||
(2304),
|
||||
(2336),
|
||||
(2368),
|
||||
(2400),
|
||||
(2432),
|
||||
(2464),
|
||||
(2496),
|
||||
(2528),
|
||||
(2560),
|
||||
(2592),
|
||||
(2624),
|
||||
(2656),
|
||||
(2688),
|
||||
(2720),
|
||||
(2752),
|
||||
(2784),
|
||||
(2816),
|
||||
(2848),
|
||||
(2880),
|
||||
(2912),
|
||||
(2944),
|
||||
(2976),
|
||||
(3008),
|
||||
(3040),
|
||||
(3072)
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaModularExponentiation`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 19.3.1 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn new(
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
exponent: &T::InputType,
|
||||
modulus: &T::InputType,
|
||||
m_prime: u32,
|
||||
) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_operand_b(exponent);
|
||||
rsa.write_modulus(modulus);
|
||||
}
|
||||
rsa.write_mprime(m_prime);
|
||||
if rsa.is_search_enabled() {
|
||||
rsa.write_search_position(Self::find_search_pos(exponent));
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_search_pos(exponent: &T::InputType) -> u32 {
|
||||
for (i, byte) in exponent.iter().rev().enumerate() {
|
||||
if *byte == 0 {
|
||||
continue;
|
||||
}
|
||||
return (exponent.len() * 8) as u32 - (byte.leading_zeros() + i as u32 * 8) - 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 4 - 1) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_modexp_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
fn write_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 4 - 1) as u32)
|
||||
}
|
||||
|
||||
/// Creates an Instance of `RsaModularMultiplication`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 19.3.1 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn new(
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
operand_a: &T::InputType,
|
||||
operand_b: &T::InputType,
|
||||
modulus: &T::InputType,
|
||||
m_prime: u32,
|
||||
) -> Self {
|
||||
Self::write_mode(rsa);
|
||||
rsa.write_mprime(m_prime);
|
||||
unsafe {
|
||||
rsa.write_modulus(modulus);
|
||||
rsa.write_operand_a(operand_a);
|
||||
rsa.write_operand_b(operand_b);
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the modular multiplication operation. `r` could be calculated
|
||||
/// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information
|
||||
/// check 19.3.1 in the <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn start_modular_multiplication(&mut self, r: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_r(r);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
fn set_start(&mut self) {
|
||||
self.rsa.write_modmulti_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaMultiplication`.
|
||||
pub fn new(rsa: &'a mut Rsa<'d>, operand_a: &T::InputType) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_operand_a(operand_a);
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the multiplication operation.
|
||||
pub fn start_multiplication(&mut self, operand_b: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_multi_operand_b(operand_b);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 2 - 1) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_multi_start();
|
||||
}
|
||||
}
|
||||
@ -1,386 +0,0 @@
|
||||
use core::{convert::Infallible, marker::PhantomData, ptr::copy_nonoverlapping};
|
||||
|
||||
use crate::rsa::{
|
||||
implement_op,
|
||||
Multi,
|
||||
Rsa,
|
||||
RsaMode,
|
||||
RsaModularExponentiation,
|
||||
RsaModularMultiplication,
|
||||
RsaMultiplication,
|
||||
};
|
||||
|
||||
impl<'d> Rsa<'d> {
|
||||
/// After the RSA Accelerator is released from reset, the memory blocks
|
||||
/// needs to be initialized, only after that peripheral should be used.
|
||||
/// This function would return without an error if the memory is initialized
|
||||
pub fn ready(&mut self) -> nb::Result<(), Infallible> {
|
||||
if self.rsa.clean.read().clean().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enables/disables rsa interrupt, when enabled rsa perpheral would
|
||||
/// generate an interrupt when a operation is finished.
|
||||
pub fn enable_disable_interrupt(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self
|
||||
.rsa
|
||||
.interrupt_ena
|
||||
.write(|w| w.interrupt_ena().set_bit()),
|
||||
false => self
|
||||
.rsa
|
||||
.interrupt_ena
|
||||
.write(|w| w.interrupt_ena().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mode(&mut self, mode: u32) {
|
||||
Self::write_to_register(&mut self.rsa.mode, mode as u32);
|
||||
}
|
||||
|
||||
/// Enables/disables search acceleration, when enabled it would increases
|
||||
/// the performance of modular exponentiation by discarding the
|
||||
/// exponent's bits before the most significant set bit. Note: this might
|
||||
/// affect the security, for more info refer 18.3.4 of <https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf>
|
||||
pub fn enable_disable_search_acceleration(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self
|
||||
.rsa
|
||||
.search_enable
|
||||
.write(|w| w.search_enable().set_bit()),
|
||||
false => self
|
||||
.rsa
|
||||
.search_enable
|
||||
.write(|w| w.search_enable().clear_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_search_enabled(&mut self) -> bool {
|
||||
self.rsa.search_enable.read().search_enable().bit_is_set()
|
||||
}
|
||||
|
||||
pub(super) fn write_search_position(&mut self, search_position: u32) {
|
||||
Self::write_to_register(&mut self.rsa.search_pos, search_position);
|
||||
}
|
||||
|
||||
/// Enables/disables constant time acceleration, when enabled it would
|
||||
/// increases the performance of modular exponentiation by simplifying
|
||||
/// the calculation concerning the 0 bits of the exponent i.e. lesser the
|
||||
/// hamming weight, greater the performance. Note : this might affect
|
||||
/// the security, for more info refer 18.3.4 of <https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf>
|
||||
pub fn enable_disable_constant_time_acceleration(&mut self, enable: bool) {
|
||||
match enable {
|
||||
true => self
|
||||
.rsa
|
||||
.constant_time
|
||||
.write(|w| w.constant_time().clear_bit()),
|
||||
false => self
|
||||
.rsa
|
||||
.constant_time
|
||||
.write(|w| w.constant_time().set_bit()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_modexp_start(&mut self) {
|
||||
self.rsa.modexp_start.write(|w| w.modexp_start().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn write_multi_start(&mut self) {
|
||||
self.rsa.mult_start.write(|w| w.mult_start().set_bit());
|
||||
}
|
||||
|
||||
fn write_modmulti_start(&mut self) {
|
||||
self.rsa
|
||||
.modmult_start
|
||||
.write(|w| w.modmult_start().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn clear_interrupt(&mut self) {
|
||||
self.rsa
|
||||
.clear_interrupt
|
||||
.write(|w| w.clear_interrupt().set_bit());
|
||||
}
|
||||
|
||||
pub(super) fn is_idle(&mut self) -> bool {
|
||||
self.rsa.idle.read().idle().bit_is_set()
|
||||
}
|
||||
|
||||
unsafe fn write_multi_operand_b<const N: usize>(&mut self, operand_b: &[u8; N]) {
|
||||
copy_nonoverlapping(
|
||||
operand_b.as_ptr(),
|
||||
self.rsa.z_mem.as_mut_ptr().add(N) as *mut u8,
|
||||
N,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod operand_sizes {
|
||||
//! Marker types for the operand sizes
|
||||
use paste::paste;
|
||||
|
||||
use super::{implement_op, Multi, RsaMode};
|
||||
|
||||
implement_op!(
|
||||
(32, multi),
|
||||
(64, multi),
|
||||
(96, multi),
|
||||
(128, multi),
|
||||
(160, multi),
|
||||
(192, multi),
|
||||
(224, multi),
|
||||
(256, multi),
|
||||
(288, multi),
|
||||
(320, multi),
|
||||
(352, multi),
|
||||
(384, multi),
|
||||
(416, multi),
|
||||
(448, multi),
|
||||
(480, multi),
|
||||
(512, multi),
|
||||
(544, multi),
|
||||
(576, multi),
|
||||
(608, multi),
|
||||
(640, multi),
|
||||
(672, multi),
|
||||
(704, multi),
|
||||
(736, multi),
|
||||
(768, multi),
|
||||
(800, multi),
|
||||
(832, multi),
|
||||
(864, multi),
|
||||
(896, multi),
|
||||
(928, multi),
|
||||
(960, multi),
|
||||
(992, multi),
|
||||
(1024, multi),
|
||||
(1056, multi),
|
||||
(1088, multi),
|
||||
(1120, multi),
|
||||
(1152, multi),
|
||||
(1184, multi),
|
||||
(1216, multi),
|
||||
(1248, multi),
|
||||
(1280, multi),
|
||||
(1312, multi),
|
||||
(1344, multi),
|
||||
(1376, multi),
|
||||
(1408, multi),
|
||||
(1440, multi),
|
||||
(1472, multi),
|
||||
(1504, multi),
|
||||
(1536, multi),
|
||||
(1568, multi),
|
||||
(1600, multi),
|
||||
(1632, multi),
|
||||
(1664, multi),
|
||||
(1696, multi),
|
||||
(1728, multi),
|
||||
(1760, multi),
|
||||
(1792, multi),
|
||||
(1824, multi),
|
||||
(1856, multi),
|
||||
(1888, multi),
|
||||
(1920, multi),
|
||||
(1952, multi),
|
||||
(1984, multi),
|
||||
(2016, multi),
|
||||
(2048, multi)
|
||||
);
|
||||
|
||||
implement_op!(
|
||||
(2080),
|
||||
(2112),
|
||||
(2144),
|
||||
(2176),
|
||||
(2208),
|
||||
(2240),
|
||||
(2272),
|
||||
(2304),
|
||||
(2336),
|
||||
(2368),
|
||||
(2400),
|
||||
(2432),
|
||||
(2464),
|
||||
(2496),
|
||||
(2528),
|
||||
(2560),
|
||||
(2592),
|
||||
(2624),
|
||||
(2656),
|
||||
(2688),
|
||||
(2720),
|
||||
(2752),
|
||||
(2784),
|
||||
(2816),
|
||||
(2848),
|
||||
(2880),
|
||||
(2912),
|
||||
(2944),
|
||||
(2976),
|
||||
(3008),
|
||||
(3040),
|
||||
(3072),
|
||||
(3104),
|
||||
(3136),
|
||||
(3168),
|
||||
(3200),
|
||||
(3232),
|
||||
(3264),
|
||||
(3296),
|
||||
(3328),
|
||||
(3360),
|
||||
(3392),
|
||||
(3424),
|
||||
(3456),
|
||||
(3488),
|
||||
(3520),
|
||||
(3552),
|
||||
(3584),
|
||||
(3616),
|
||||
(3648),
|
||||
(3680),
|
||||
(3712),
|
||||
(3744),
|
||||
(3776),
|
||||
(3808),
|
||||
(3840),
|
||||
(3872),
|
||||
(3904),
|
||||
(3936),
|
||||
(3968),
|
||||
(4000),
|
||||
(4032),
|
||||
(4064),
|
||||
(4096)
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaModularExponentiation`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 19.3.1 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn new(
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
exponent: &T::InputType,
|
||||
modulus: &T::InputType,
|
||||
m_prime: u32,
|
||||
) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_operand_b(exponent);
|
||||
rsa.write_modulus(modulus);
|
||||
}
|
||||
rsa.write_mprime(m_prime);
|
||||
if rsa.is_search_enabled() {
|
||||
rsa.write_search_position(Self::find_search_pos(exponent));
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_search_pos(exponent: &T::InputType) -> u32 {
|
||||
for (i, byte) in exponent.iter().rev().enumerate() {
|
||||
if *byte == 0 {
|
||||
continue;
|
||||
}
|
||||
return (exponent.len() * 8) as u32 - (byte.leading_zeros() + i as u32 * 8) - 1;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 4 - 1) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_modexp_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaModularMultiplication`.
|
||||
/// `m_prime` could be calculated using `-(modular multiplicative inverse of
|
||||
/// modulus) mod 2^32`, for more information check 19.3.1 in the
|
||||
/// <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn new(
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
operand_a: &T::InputType,
|
||||
operand_b: &T::InputType,
|
||||
modulus: &T::InputType,
|
||||
m_prime: u32,
|
||||
) -> Self {
|
||||
Self::write_mode(rsa);
|
||||
rsa.write_mprime(m_prime);
|
||||
unsafe {
|
||||
rsa.write_modulus(modulus);
|
||||
rsa.write_operand_a(operand_a);
|
||||
rsa.write_operand_b(operand_b);
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn write_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 4 - 1) as u32)
|
||||
}
|
||||
|
||||
/// Starts the modular multiplication operation. `r` could be calculated
|
||||
/// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information
|
||||
/// check 19.3.1 in the <https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf>
|
||||
pub fn start_modular_multiplication(&mut self, r: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_r(r);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
fn set_start(&mut self) {
|
||||
self.rsa.write_modmulti_start();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Creates an Instance of `RsaMultiplication`.
|
||||
pub fn new(rsa: &'a mut Rsa<'d>, operand_a: &T::InputType) -> Self {
|
||||
Self::set_mode(rsa);
|
||||
unsafe {
|
||||
rsa.write_operand_a(operand_a);
|
||||
}
|
||||
Self {
|
||||
rsa,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the multiplication operation.
|
||||
pub fn start_multiplication(&mut self, operand_b: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_multi_operand_b(operand_b);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
pub(super) fn set_mode(rsa: &mut Rsa) {
|
||||
rsa.write_mode((N / 2 - 1) as u32)
|
||||
}
|
||||
|
||||
pub(super) fn set_start(&mut self) {
|
||||
self.rsa.write_multi_start();
|
||||
}
|
||||
}
|
||||
@ -1,229 +0,0 @@
|
||||
//! RSA Accelerator support.
|
||||
//!
|
||||
//! This module provides functions and structs for multi precision arithmetic
|
||||
//! operations used in RSA asym-metric cipher algorithms
|
||||
|
||||
use core::{convert::Infallible, marker::PhantomData, ptr::copy_nonoverlapping};
|
||||
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals::{
|
||||
generic::{Reg, RegisterSpec, Resettable, Writable},
|
||||
RSA,
|
||||
},
|
||||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||||
};
|
||||
|
||||
#[cfg_attr(esp32s2, path = "esp32sX.rs")]
|
||||
#[cfg_attr(esp32s3, path = "esp32sX.rs")]
|
||||
#[cfg_attr(esp32c3, path = "esp32cX.rs")]
|
||||
#[cfg_attr(esp32c6, path = "esp32cX.rs")]
|
||||
#[cfg_attr(esp32h2, path = "esp32cX.rs")]
|
||||
#[cfg_attr(esp32, path = "esp32.rs")]
|
||||
mod rsa_spec_impl;
|
||||
|
||||
pub use rsa_spec_impl::operand_sizes;
|
||||
|
||||
/// RSA peripheral container
|
||||
pub struct Rsa<'d> {
|
||||
rsa: PeripheralRef<'d, RSA>,
|
||||
}
|
||||
|
||||
impl<'d> Rsa<'d> {
|
||||
pub fn new(
|
||||
rsa: impl Peripheral<P = RSA> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(rsa);
|
||||
let mut ret = Self { rsa };
|
||||
ret.init(peripheral_clock_control);
|
||||
ret
|
||||
}
|
||||
|
||||
fn init(&mut self, peripheral_clock_control: &mut PeripheralClockControl) {
|
||||
peripheral_clock_control.enable(PeripheralEnable::Rsa);
|
||||
}
|
||||
|
||||
unsafe fn write_operand_b<const N: usize>(&mut self, operand_b: &[u8; N]) {
|
||||
copy_nonoverlapping(
|
||||
operand_b.as_ptr(),
|
||||
self.rsa.y_mem.as_mut_ptr() as *mut u8,
|
||||
N,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn write_modulus<const N: usize>(&mut self, modulus: &[u8; N]) {
|
||||
copy_nonoverlapping(modulus.as_ptr(), self.rsa.m_mem.as_mut_ptr() as *mut u8, N);
|
||||
}
|
||||
|
||||
fn write_mprime(&mut self, m_prime: u32) {
|
||||
Self::write_to_register(&mut self.rsa.m_prime, m_prime);
|
||||
}
|
||||
|
||||
unsafe fn write_operand_a<const N: usize>(&mut self, operand_a: &[u8; N]) {
|
||||
copy_nonoverlapping(
|
||||
operand_a.as_ptr(),
|
||||
self.rsa.x_mem.as_mut_ptr() as *mut u8,
|
||||
N,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn write_r<const N: usize>(&mut self, r: &[u8; N]) {
|
||||
copy_nonoverlapping(r.as_ptr(), self.rsa.z_mem.as_mut_ptr() as *mut u8, N);
|
||||
}
|
||||
|
||||
unsafe fn read_out<const N: usize>(&mut self, outbuf: &mut [u8; N]) {
|
||||
copy_nonoverlapping(self.rsa.z_mem.as_ptr() as *const u8, outbuf.as_mut_ptr(), N);
|
||||
}
|
||||
|
||||
fn write_to_register<T>(reg: &mut Reg<T>, data: u32)
|
||||
where
|
||||
T: RegisterSpec<Ux = u32> + Resettable + Writable,
|
||||
{
|
||||
reg.write(|w| unsafe { w.bits(data) });
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait RsaMode {
|
||||
type InputType;
|
||||
}
|
||||
pub trait Multi: RsaMode {
|
||||
type OutputType;
|
||||
}
|
||||
}
|
||||
|
||||
pub(self) use sealed::*;
|
||||
|
||||
macro_rules! implement_op {
|
||||
|
||||
(($x:literal, multi)) => {
|
||||
paste! {pub struct [<Op $x>];}
|
||||
paste! {
|
||||
impl Multi for [<Op $x>] {
|
||||
type OutputType = [u8; $x*2 / 8];
|
||||
}}
|
||||
paste!{
|
||||
impl RsaMode for [<Op $x>] {
|
||||
type InputType = [u8; $x / 8];
|
||||
}}
|
||||
};
|
||||
|
||||
(($x:literal)) => {
|
||||
paste! {pub struct [<Op $x>];}
|
||||
paste!{
|
||||
impl RsaMode for [<Op $x>] {
|
||||
type InputType = [u8; $x / 8];
|
||||
}}
|
||||
};
|
||||
|
||||
($x:tt, $($y:tt),+) => {
|
||||
implement_op!($x);
|
||||
implement_op!($($y),+);
|
||||
};
|
||||
}
|
||||
|
||||
pub(self) use implement_op;
|
||||
|
||||
/// Support for RSA peripheral's modular exponentiation feature that could be
|
||||
/// used to find the `(base ^ exponent) mod modulus`.
|
||||
///
|
||||
/// Each operand is a little endian byte array of the same size
|
||||
pub struct RsaModularExponentiation<'a, 'd, T: RsaMode> {
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// starts the modular exponentiation operation. `r` could be calculated
|
||||
/// using `2 ^ ( bitlength * 2 ) mod modulus`, for more information
|
||||
/// check 24.3.2 in the <https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf>
|
||||
pub fn start_exponentiation(&mut self, base: &T::InputType, r: &T::InputType) {
|
||||
unsafe {
|
||||
self.rsa.write_operand_a(base);
|
||||
self.rsa.write_r(r);
|
||||
}
|
||||
self.set_start();
|
||||
}
|
||||
|
||||
/// reads the result to the given buffer.
|
||||
/// This is a non blocking function that returns without an error if
|
||||
/// operation is completed successfully. `start_exponentiation` must be
|
||||
/// called before calling this function.
|
||||
pub fn read_results(&mut self, outbuf: &mut T::InputType) -> nb::Result<(), Infallible> {
|
||||
if !self.rsa.is_idle() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
unsafe {
|
||||
self.rsa.read_out(outbuf);
|
||||
}
|
||||
self.rsa.clear_interrupt();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Support for RSA peripheral's modular multiplication feature that could be
|
||||
/// used to find the `(operand a * operand b) mod modulus`.
|
||||
///
|
||||
/// Each operand is a little endian byte array of the same size
|
||||
pub struct RsaModularMultiplication<'a, 'd, T: RsaMode> {
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Reads the result to the given buffer.
|
||||
/// This is a non blocking function that returns without an error if
|
||||
/// operation is completed successfully.
|
||||
pub fn read_results(&mut self, outbuf: &mut T::InputType) -> nb::Result<(), Infallible> {
|
||||
if !self.rsa.is_idle() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
unsafe {
|
||||
self.rsa.read_out(outbuf);
|
||||
}
|
||||
self.rsa.clear_interrupt();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Support for RSA peripheral's large number multiplication feature that could
|
||||
/// be used to find the `operand a * operand b`.
|
||||
///
|
||||
/// Each operand is a little endian byte array of the same size
|
||||
pub struct RsaMultiplication<'a, 'd, T: RsaMode + Multi> {
|
||||
rsa: &'a mut Rsa<'d>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T>
|
||||
where
|
||||
T: RsaMode<InputType = [u8; N]>,
|
||||
{
|
||||
/// Reads the result to the given buffer.
|
||||
/// This is a non blocking function that returns without an error if
|
||||
/// operation is completed successfully. `start_multiplication` must be
|
||||
/// called before calling this function.
|
||||
pub fn read_results<'b, const O: usize>(
|
||||
&mut self,
|
||||
outbuf: &mut T::OutputType,
|
||||
) -> nb::Result<(), Infallible>
|
||||
where
|
||||
T: Multi<OutputType = [u8; O]>,
|
||||
{
|
||||
if !self.rsa.is_idle() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
unsafe {
|
||||
self.rsa.read_out(outbuf);
|
||||
}
|
||||
self.rsa.clear_interrupt();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,878 +0,0 @@
|
||||
use embedded_hal::watchdog::{Watchdog, WatchdogDisable, WatchdogEnable};
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
use fugit::HertzU32;
|
||||
use fugit::MicrosDurationU64;
|
||||
|
||||
pub use self::rtc::SocResetReason;
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
use crate::clock::XtalClock;
|
||||
#[cfg(not(esp32))]
|
||||
use crate::efuse::Efuse;
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
use crate::peripherals::{LP_TIMER, LP_WDT};
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
use crate::peripherals::{RTC_CNTL, TIMG0};
|
||||
use crate::{
|
||||
clock::Clock,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
reset::{SleepSource, WakeupReason},
|
||||
Cpu,
|
||||
};
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
type RtcCntl = crate::peripherals::LP_CLKRST;
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
type RtcCntl = crate::peripherals::RTC_CNTL;
|
||||
|
||||
#[cfg_attr(esp32, path = "rtc/esp32.rs")]
|
||||
#[cfg_attr(esp32c2, path = "rtc/esp32c2.rs")]
|
||||
#[cfg_attr(esp32c3, path = "rtc/esp32c3.rs")]
|
||||
#[cfg_attr(esp32c6, path = "rtc/esp32c6.rs")]
|
||||
#[cfg_attr(esp32h2, path = "rtc/esp32h2.rs")]
|
||||
#[cfg_attr(esp32s2, path = "rtc/esp32s2.rs")]
|
||||
#[cfg_attr(esp32s3, path = "rtc/esp32s3.rs")]
|
||||
mod rtc;
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
pub use rtc::RtcClock;
|
||||
|
||||
extern "C" {
|
||||
#[allow(dead_code)]
|
||||
fn ets_delay_us(us: u32);
|
||||
fn rtc_get_reset_reason(cpu_num: u32) -> u32;
|
||||
pub fn software_reset_cpu();
|
||||
pub fn software_reset();
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// RTC SLOW_CLK frequency values
|
||||
pub(crate) enum RtcFastClock {
|
||||
/// Main XTAL, divided by 4
|
||||
RtcFastClockXtalD4 = 0,
|
||||
/// Internal fast RC oscillator
|
||||
RtcFastClock8m = 1,
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
impl Clock for RtcFastClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
RtcFastClock::RtcFastClockXtalD4 => HertzU32::Hz(40_000_000 / 4),
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
RtcFastClock::RtcFastClock8m => HertzU32::Hz(8_500_000),
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
RtcFastClock::RtcFastClock8m => HertzU32::Hz(17_500_000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// RTC SLOW_CLK frequency values
|
||||
pub(crate) enum RtcSlowClock {
|
||||
/// Internal slow RC oscillator
|
||||
RtcSlowClockRtc = 0,
|
||||
/// External 32 KHz XTAL
|
||||
RtcSlowClock32kXtal = 1,
|
||||
/// Internal fast RC oscillator, divided by 256
|
||||
RtcSlowClock8mD256 = 2,
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
impl Clock for RtcSlowClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
#[cfg(esp32)]
|
||||
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(150_000),
|
||||
#[cfg(esp32s2)]
|
||||
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(90_000),
|
||||
#[cfg(any(esp32c2, esp32c3, esp32s3))]
|
||||
RtcSlowClock::RtcSlowClockRtc => HertzU32::Hz(136_000),
|
||||
RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768),
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(8_500_000 / 256),
|
||||
#[cfg(any(esp32c2, esp32c3, esp32s3))]
|
||||
RtcSlowClock::RtcSlowClock8mD256 => HertzU32::Hz(17_500_000 / 256),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// Clock source to be calibrated using rtc_clk_cal function
|
||||
pub(crate) enum RtcCalSel {
|
||||
/// Currently selected RTC SLOW_CLK
|
||||
RtcCalRtcMux = 0,
|
||||
/// Internal 8 MHz RC oscillator, divided by 256
|
||||
RtcCal8mD256 = 1,
|
||||
/// External 32 KHz XTAL
|
||||
RtcCal32kXtal = 2,
|
||||
#[cfg(not(esp32))]
|
||||
/// Internal 150 KHz RC oscillator
|
||||
RtcCalInternalOsc = 3,
|
||||
}
|
||||
|
||||
pub struct Rtc<'d> {
|
||||
_inner: PeripheralRef<'d, RtcCntl>,
|
||||
pub rwdt: Rwdt,
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
pub swd: Swd,
|
||||
}
|
||||
|
||||
impl<'d> Rtc<'d> {
|
||||
pub fn new(rtc_cntl: impl Peripheral<P = RtcCntl> + 'd) -> Self {
|
||||
rtc::init();
|
||||
rtc::configure_clock();
|
||||
|
||||
Self {
|
||||
_inner: rtc_cntl.into_ref(),
|
||||
rwdt: Rwdt::default(),
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
swd: Swd::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement for ESP32-C6
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
pub fn estimate_xtal_frequency(&mut self) -> u32 {
|
||||
RtcClock::estimate_xtal_frequency()
|
||||
}
|
||||
|
||||
/// read the current value of the rtc time registers.
|
||||
pub fn get_time_raw(&self) -> u64 {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::ptr() };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_TIMER::ptr() };
|
||||
|
||||
#[cfg(esp32)]
|
||||
let (l, h) = {
|
||||
rtc_cntl.time_update.write(|w| w.time_update().set_bit());
|
||||
while rtc_cntl.time_update.read().time_valid().bit_is_clear() {
|
||||
unsafe {
|
||||
// might take 1 RTC slowclk period, don't flood RTC bus
|
||||
ets_delay_us(1);
|
||||
}
|
||||
}
|
||||
let h = rtc_cntl.time1.read().time_hi().bits();
|
||||
let l = rtc_cntl.time0.read().time_lo().bits();
|
||||
(l, h)
|
||||
};
|
||||
#[cfg(any(esp32c2, esp32c3, esp32s3, esp32s2))]
|
||||
let (l, h) = {
|
||||
rtc_cntl.time_update.write(|w| w.time_update().set_bit());
|
||||
let h = rtc_cntl.time_high0.read().timer_value0_high().bits();
|
||||
let l = rtc_cntl.time_low0.read().timer_value0_low().bits();
|
||||
(l, h)
|
||||
};
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let (l, h) = {
|
||||
rtc_cntl.update.write(|w| w.main_timer_update().set_bit());
|
||||
let h = rtc_cntl.main_buf0_high.read().main_timer_buf0_high().bits();
|
||||
let l = rtc_cntl.main_buf0_low.read().main_timer_buf0_low().bits();
|
||||
(l, h)
|
||||
};
|
||||
((h as u64) << 32) | (l as u64)
|
||||
}
|
||||
|
||||
/// read the current value of the rtc time registers in microseconds.
|
||||
pub fn get_time_us(&self) -> u64 {
|
||||
self.get_time_raw() * 1_000_000 / RtcClock::get_slow_freq().frequency().to_Hz() as u64
|
||||
}
|
||||
|
||||
/// read the current value of the rtc time registers in milliseconds
|
||||
pub fn get_time_ms(&self) -> u64 {
|
||||
self.get_time_raw() * 1_000 / RtcClock::get_slow_freq().frequency().to_Hz() as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
/// RTC Watchdog Timer
|
||||
pub struct RtcClock;
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
/// RTC Watchdog Timer driver
|
||||
impl RtcClock {
|
||||
const CAL_FRACT: u32 = 19;
|
||||
|
||||
/// Enable or disable 8 MHz internal oscillator
|
||||
///
|
||||
/// Output from 8 MHz internal oscillator is passed into a configurable
|
||||
/// divider, which by default divides the input clock frequency by 256.
|
||||
/// Output of the divider may be used as RTC_SLOW_CLK source.
|
||||
/// Output of the divider is referred to in register descriptions and code
|
||||
/// as 8md256 or simply d256. Divider values other than 256 may be
|
||||
/// configured, but this facility is not currently needed, so is not
|
||||
/// exposed in the code.
|
||||
///
|
||||
/// When 8MHz/256 divided output is not needed, the divider should be
|
||||
/// disabled to reduce power consumption.
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn enable_8m(clk_8m_en: bool, d256_en: bool) {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
|
||||
if clk_8m_en {
|
||||
rtc_cntl.clk_conf.modify(|_, w| w.enb_ck8m().clear_bit());
|
||||
unsafe {
|
||||
rtc_cntl.timer1.modify(|_, w| w.ck8m_wait().bits(5));
|
||||
ets_delay_us(50);
|
||||
}
|
||||
} else {
|
||||
rtc_cntl.clk_conf.modify(|_, w| w.enb_ck8m().set_bit());
|
||||
rtc_cntl
|
||||
.timer1
|
||||
.modify(|_, w| unsafe { w.ck8m_wait().bits(20) });
|
||||
}
|
||||
|
||||
if d256_en {
|
||||
rtc_cntl
|
||||
.clk_conf
|
||||
.modify(|_, w| w.enb_ck8m_div().clear_bit());
|
||||
} else {
|
||||
rtc_cntl.clk_conf.modify(|_, w| w.enb_ck8m_div().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
/// Get main XTAL frequency
|
||||
/// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the
|
||||
/// bootloader, as passed to rtc_clk_init function.
|
||||
fn get_xtal_freq() -> XtalClock {
|
||||
let xtal_freq_reg = unsafe { &*RTC_CNTL::PTR }.store4.read().bits();
|
||||
|
||||
// Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in
|
||||
// lower and upper 16-bit halves. These are the routines to work with such a
|
||||
// representation.
|
||||
let clk_val_is_valid = |val| {
|
||||
(val & 0xffffu32) == ((val >> 16u32) & 0xffffu32) && val != 0u32 && val != u32::MAX
|
||||
};
|
||||
let reg_val_to_clk_val = |val| val & u16::MAX as u32;
|
||||
|
||||
if !clk_val_is_valid(xtal_freq_reg) {
|
||||
return XtalClock::RtcXtalFreq40M;
|
||||
}
|
||||
|
||||
match reg_val_to_clk_val(xtal_freq_reg) {
|
||||
40 => XtalClock::RtcXtalFreq40M,
|
||||
#[cfg(any(esp32c3, esp32s3))]
|
||||
32 => XtalClock::RtcXtalFreq32M,
|
||||
#[cfg(any(esp32, esp32c2))]
|
||||
26 => XtalClock::RtcXtalFreq26M,
|
||||
#[cfg(esp32)]
|
||||
24 => XtalClock::RtcXtalFreq24M,
|
||||
other => XtalClock::RtcXtalFreqOther(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RTC_SLOW_CLK source
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn get_slow_freq() -> RtcSlowClock {
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
let slow_freq = rtc_cntl.clk_conf.read().ana_clk_rtc_sel().bits();
|
||||
match slow_freq {
|
||||
0 => RtcSlowClock::RtcSlowClockRtc,
|
||||
1 => RtcSlowClock::RtcSlowClock32kXtal,
|
||||
2 => RtcSlowClock::RtcSlowClock8mD256,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Select source for RTC_SLOW_CLK
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn set_slow_freq(slow_freq: RtcSlowClock) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::PTR;
|
||||
rtc_cntl.clk_conf.modify(|_, w| {
|
||||
w.ana_clk_rtc_sel()
|
||||
.bits(slow_freq as u8)
|
||||
// Why we need to connect this clock to digital?
|
||||
// Or maybe this clock should be connected to digital when
|
||||
// XTAL 32k clock is enabled instead?
|
||||
.dig_xtal32k_en()
|
||||
.bit(match slow_freq {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => true,
|
||||
_ => false,
|
||||
})
|
||||
// The clk_8m_d256 will be closed when rtc_state in SLEEP,
|
||||
// so if the slow_clk is 8md256, clk_8m must be force power on
|
||||
.ck8m_force_pu()
|
||||
.bit(match slow_freq {
|
||||
RtcSlowClock::RtcSlowClock8mD256 => true,
|
||||
_ => false,
|
||||
})
|
||||
});
|
||||
|
||||
ets_delay_us(300u32);
|
||||
};
|
||||
}
|
||||
|
||||
/// Select source for RTC_FAST_CLK
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn set_fast_freq(fast_freq: RtcFastClock) {
|
||||
unsafe {
|
||||
let rtc_cntl = &*RTC_CNTL::PTR;
|
||||
rtc_cntl.clk_conf.modify(|_, w| {
|
||||
w.fast_clk_rtc_sel().bit(match fast_freq {
|
||||
RtcFastClock::RtcFastClock8m => true,
|
||||
RtcFastClock::RtcFastClockXtalD4 => false,
|
||||
})
|
||||
});
|
||||
|
||||
ets_delay_us(3u32);
|
||||
};
|
||||
}
|
||||
|
||||
/// Calibration of RTC_SLOW_CLK is performed using a special feature of
|
||||
/// TIMG0. This feature counts the number of XTAL clock cycles within a
|
||||
/// given number of RTC_SLOW_CLK cycles.
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn calibrate_internal(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
// Except for ESP32, choosing RTC_CAL_RTC_MUX results in calibration of
|
||||
// the 150k RTC clock (90k on ESP32-S2) regardless of the currently selected
|
||||
// SLOW_CLK. On the ESP32, it uses the currently selected SLOW_CLK.
|
||||
// The following code emulates ESP32 behavior for the other chips:
|
||||
#[cfg(not(esp32))]
|
||||
let cal_clk = match cal_clk {
|
||||
RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256,
|
||||
_ => cal_clk,
|
||||
},
|
||||
RtcCalSel::RtcCalInternalOsc => RtcCalSel::RtcCalRtcMux,
|
||||
_ => cal_clk,
|
||||
};
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
let timg0 = unsafe { &*TIMG0::PTR };
|
||||
|
||||
// Enable requested clock (150k clock is always on)
|
||||
let dig_32k_xtal_enabled = rtc_cntl.clk_conf.read().dig_xtal32k_en().bit_is_set();
|
||||
|
||||
if matches!(cal_clk, RtcCalSel::RtcCal32kXtal) && !dig_32k_xtal_enabled {
|
||||
rtc_cntl
|
||||
.clk_conf
|
||||
.modify(|_, w| w.dig_xtal32k_en().set_bit());
|
||||
}
|
||||
|
||||
if matches!(cal_clk, RtcCalSel::RtcCal8mD256) {
|
||||
rtc_cntl
|
||||
.clk_conf
|
||||
.modify(|_, w| w.dig_clk8m_d256_en().set_bit());
|
||||
}
|
||||
|
||||
// There may be another calibration process already running during we
|
||||
// call this function, so we should wait the last process is done.
|
||||
#[cfg(not(esp32))]
|
||||
if timg0
|
||||
.rtccalicfg
|
||||
.read()
|
||||
.rtc_cali_start_cycling()
|
||||
.bit_is_set()
|
||||
{
|
||||
// Set a small timeout threshold to accelerate the generation of timeout.
|
||||
// The internal circuit will be reset when the timeout occurs and will not
|
||||
// affect the next calibration.
|
||||
timg0
|
||||
.rtccalicfg2
|
||||
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) });
|
||||
|
||||
while timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_clear()
|
||||
&& timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_clear()
|
||||
{}
|
||||
}
|
||||
|
||||
// Prepare calibration
|
||||
timg0.rtccalicfg.modify(|_, w| unsafe {
|
||||
w.rtc_cali_clk_sel()
|
||||
.bits(cal_clk as u8)
|
||||
.rtc_cali_start_cycling()
|
||||
.clear_bit()
|
||||
.rtc_cali_max()
|
||||
.bits(slowclk_cycles as u16)
|
||||
});
|
||||
|
||||
// Figure out how long to wait for calibration to finish
|
||||
// Set timeout reg and expect time delay
|
||||
let expected_freq = match cal_clk {
|
||||
RtcCalSel::RtcCal32kXtal => {
|
||||
#[cfg(not(esp32))]
|
||||
timg0.rtccalicfg2.modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
|
||||
});
|
||||
RtcSlowClock::RtcSlowClock32kXtal
|
||||
}
|
||||
RtcCalSel::RtcCal8mD256 => {
|
||||
#[cfg(not(esp32))]
|
||||
timg0.rtccalicfg2.modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
|
||||
});
|
||||
RtcSlowClock::RtcSlowClock8mD256
|
||||
}
|
||||
_ => {
|
||||
#[cfg(not(esp32))]
|
||||
timg0.rtccalicfg2.modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 10)
|
||||
});
|
||||
RtcSlowClock::RtcSlowClockRtc
|
||||
}
|
||||
};
|
||||
|
||||
let us_time_estimate = HertzU32::MHz(slowclk_cycles) / expected_freq.frequency();
|
||||
|
||||
// Start calibration
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit().rtc_cali_start().set_bit());
|
||||
|
||||
// Wait for calibration to finish up to another us_time_estimate
|
||||
unsafe {
|
||||
ets_delay_us(us_time_estimate);
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
let mut timeout_us = us_time_estimate;
|
||||
|
||||
let cal_val = loop {
|
||||
if timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_set() {
|
||||
break timg0.rtccalicfg1.read().rtc_cali_value().bits();
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
if timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_set() {
|
||||
// Timed out waiting for calibration
|
||||
break 0;
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
if timeout_us > 0 {
|
||||
timeout_us -= 1;
|
||||
unsafe {
|
||||
ets_delay_us(1);
|
||||
}
|
||||
} else {
|
||||
// Timed out waiting for calibration
|
||||
break 0;
|
||||
}
|
||||
};
|
||||
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit());
|
||||
rtc_cntl
|
||||
.clk_conf
|
||||
.modify(|_, w| w.dig_xtal32k_en().bit(dig_32k_xtal_enabled));
|
||||
|
||||
if matches!(cal_clk, RtcCalSel::RtcCal8mD256) {
|
||||
rtc_cntl
|
||||
.clk_conf
|
||||
.modify(|_, w| w.dig_clk8m_d256_en().clear_bit());
|
||||
}
|
||||
|
||||
cal_val
|
||||
}
|
||||
|
||||
/// Measure ratio between XTAL frequency and RTC slow clock frequency
|
||||
fn get_calibration_ratio(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64;
|
||||
let ratio = (xtal_cycles << RtcClock::CAL_FRACT) / slowclk_cycles as u64;
|
||||
|
||||
(ratio & (u32::MAX as u64)) as u32
|
||||
}
|
||||
|
||||
/// Measure RTC slow clock's period, based on main XTAL frequency
|
||||
///
|
||||
/// This function will time out and return 0 if the time for the given
|
||||
/// number of cycles to be counted exceeds the expected time twice. This
|
||||
/// may happen if 32k XTAL is being calibrated, but the oscillator has
|
||||
/// not started up (due to incorrect loading capacitance, board design
|
||||
/// issue, or lack of 32 XTAL on board).
|
||||
fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
let xtal_freq = RtcClock::get_xtal_freq();
|
||||
let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64;
|
||||
let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64;
|
||||
let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider;
|
||||
|
||||
(period_64 & u32::MAX as u64) as u32
|
||||
}
|
||||
|
||||
/// Calculate the necessary RTC_SLOW_CLK cycles to complete 1 millisecond.
|
||||
fn cycles_to_1ms() -> u16 {
|
||||
let period_13q19 = RtcClock::calibrate(
|
||||
match RtcClock::get_slow_freq() {
|
||||
RtcSlowClock::RtcSlowClockRtc => RtcCalSel::RtcCalRtcMux,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256,
|
||||
},
|
||||
1024,
|
||||
);
|
||||
|
||||
// 100_000_000 is used to get rid of `float` calculations
|
||||
let period = (100_000_000 * period_13q19 as u64) / (1 << RtcClock::CAL_FRACT);
|
||||
|
||||
(100_000_000 * 1000 / period) as u16
|
||||
}
|
||||
|
||||
// TODO: implement for ESP32-C6
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
fn estimate_xtal_frequency() -> u32 {
|
||||
// Number of 8M/256 clock cycles to use for XTAL frequency estimation.
|
||||
const XTAL_FREQ_EST_CYCLES: u32 = 10;
|
||||
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
let clk_8m_enabled = rtc_cntl.clk_conf.read().enb_ck8m().bit_is_clear();
|
||||
let clk_8md256_enabled = rtc_cntl.clk_conf.read().enb_ck8m_div().bit_is_clear();
|
||||
|
||||
if !clk_8md256_enabled {
|
||||
RtcClock::enable_8m(true, true);
|
||||
}
|
||||
|
||||
let ratio = RtcClock::get_calibration_ratio(RtcCalSel::RtcCal8mD256, XTAL_FREQ_EST_CYCLES);
|
||||
let freq_mhz =
|
||||
((ratio as u64 * RtcFastClock::RtcFastClock8m.hz() as u64 / 1_000_000u64 / 256u64)
|
||||
>> RtcClock::CAL_FRACT) as u32;
|
||||
|
||||
RtcClock::enable_8m(clk_8m_enabled, clk_8md256_enabled);
|
||||
|
||||
freq_mhz
|
||||
}
|
||||
}
|
||||
|
||||
/// Behavior of the RWDT stage if it times out
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum RwdtStageAction {
|
||||
RwdtStageActionOff = 0,
|
||||
RwdtStageActionInterrupt = 1,
|
||||
RwdtStageActionResetCpu = 2,
|
||||
RwdtStageActionResetSystem = 3,
|
||||
RwdtStageActionResetRtc = 4,
|
||||
}
|
||||
|
||||
/// RTC Watchdog Timer
|
||||
pub struct Rwdt {
|
||||
stg0_action: RwdtStageAction,
|
||||
stg1_action: RwdtStageAction,
|
||||
stg2_action: RwdtStageAction,
|
||||
stg3_action: RwdtStageAction,
|
||||
}
|
||||
|
||||
impl Default for Rwdt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stg0_action: RwdtStageAction::RwdtStageActionResetRtc,
|
||||
stg1_action: RwdtStageAction::RwdtStageActionOff,
|
||||
stg2_action: RwdtStageAction::RwdtStageActionOff,
|
||||
stg3_action: RwdtStageAction::RwdtStageActionOff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RTC Watchdog Timer driver
|
||||
impl Rwdt {
|
||||
pub fn listen(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.stg0_action = RwdtStageAction::RwdtStageActionInterrupt;
|
||||
|
||||
self.set_write_protection(false);
|
||||
|
||||
// Configure STAGE0 to trigger an interrupt upon expiration
|
||||
rtc_cntl
|
||||
.wdtconfig0
|
||||
.modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) });
|
||||
|
||||
#[cfg(esp32)]
|
||||
rtc_cntl.int_ena.modify(|_, w| w.wdt_int_ena().set_bit());
|
||||
#[cfg(not(esp32))]
|
||||
rtc_cntl
|
||||
.int_ena_rtc
|
||||
.modify(|_, w| w.wdt_int_ena().set_bit());
|
||||
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
|
||||
pub fn unlisten(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.stg0_action = RwdtStageAction::RwdtStageActionResetRtc;
|
||||
|
||||
self.set_write_protection(false);
|
||||
|
||||
// Configure STAGE0 to reset the main system and the RTC upon expiration.
|
||||
rtc_cntl
|
||||
.wdtconfig0
|
||||
.modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) });
|
||||
|
||||
#[cfg(esp32)]
|
||||
rtc_cntl.int_ena.modify(|_, w| w.wdt_int_ena().clear_bit());
|
||||
#[cfg(not(esp32))]
|
||||
rtc_cntl
|
||||
.int_ena_rtc
|
||||
.modify(|_, w| w.wdt_int_ena().clear_bit());
|
||||
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.set_write_protection(false);
|
||||
|
||||
#[cfg(esp32)]
|
||||
rtc_cntl.int_clr.write(|w| w.wdt_int_clr().set_bit());
|
||||
#[cfg(not(esp32))]
|
||||
rtc_cntl.int_clr_rtc.write(|w| w.wdt_int_clr().set_bit());
|
||||
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
|
||||
pub fn is_interrupt_set(&self) -> bool {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32)] {
|
||||
rtc_cntl.int_st.read().wdt_int_st().bit_is_set()
|
||||
} else {
|
||||
rtc_cntl.int_st_rtc.read().wdt_int_st().bit_is_set()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable/disable write protection for WDT registers
|
||||
fn set_write_protection(&mut self, enable: bool) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
let wkey = if enable { 0u32 } else { 0x50D8_3AA1 };
|
||||
|
||||
rtc_cntl.wdtwprotect.write(|w| unsafe { w.bits(wkey) });
|
||||
}
|
||||
}
|
||||
|
||||
impl WatchdogDisable for Rwdt {
|
||||
fn disable(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.set_write_protection(false);
|
||||
|
||||
rtc_cntl
|
||||
.wdtconfig0
|
||||
.modify(|_, w| w.wdt_en().clear_bit().wdt_flashboot_mod_en().clear_bit());
|
||||
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this can be refactored
|
||||
impl WatchdogEnable for Rwdt {
|
||||
type Time = MicrosDurationU64;
|
||||
|
||||
fn start<T>(&mut self, period: T)
|
||||
where
|
||||
T: Into<Self::Time>,
|
||||
{
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
let timeout_raw = (period.into().to_millis() * (RtcClock::cycles_to_1ms() as u64)) as u32;
|
||||
self.set_write_protection(false);
|
||||
|
||||
unsafe {
|
||||
#[cfg(esp32)]
|
||||
rtc_cntl
|
||||
.wdtconfig1
|
||||
.modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw));
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
(&*LP_WDT::PTR).config1.modify(|_, w| {
|
||||
w.wdt_stg0_hold()
|
||||
.bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier()))
|
||||
});
|
||||
|
||||
#[cfg(not(any(esp32, esp32c6, esp32h2)))]
|
||||
rtc_cntl.wdtconfig1.modify(|_, w| {
|
||||
w.wdt_stg0_hold()
|
||||
.bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier()))
|
||||
});
|
||||
|
||||
rtc_cntl.wdtconfig0.modify(|_, w| {
|
||||
w.wdt_stg0()
|
||||
.bits(self.stg0_action as u8)
|
||||
.wdt_cpu_reset_length()
|
||||
.bits(7)
|
||||
.wdt_sys_reset_length()
|
||||
.bits(7)
|
||||
.wdt_stg1()
|
||||
.bits(self.stg1_action as u8)
|
||||
.wdt_stg2()
|
||||
.bits(self.stg2_action as u8)
|
||||
.wdt_stg3()
|
||||
.bits(self.stg3_action as u8)
|
||||
.wdt_en()
|
||||
.set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Watchdog for Rwdt {
|
||||
fn feed(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.set_write_protection(false);
|
||||
rtc_cntl.wdtfeed.write(|w| unsafe { w.bits(1) });
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
/// Super Watchdog
|
||||
pub struct Swd;
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
/// Super Watchdog driver
|
||||
impl Swd {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
/// Enable/disable write protection for WDT registers
|
||||
fn set_write_protection(&mut self, enable: bool) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let wkey = if enable { 0u32 } else { 0x8F1D_312A };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let wkey = if enable { 0u32 } else { 0x50D8_3AA1 };
|
||||
|
||||
rtc_cntl
|
||||
.swd_wprotect
|
||||
.write(|w| unsafe { w.swd_wkey().bits(wkey) });
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
impl WatchdogDisable for Swd {
|
||||
fn disable(&mut self) {
|
||||
#[cfg(not(any(esp32c6, esp32h2)))]
|
||||
let rtc_cntl = unsafe { &*RTC_CNTL::PTR };
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let rtc_cntl = unsafe { &*LP_WDT::PTR };
|
||||
|
||||
self.set_write_protection(false);
|
||||
rtc_cntl.swd_conf.write(|w| w.swd_auto_feed_en().set_bit());
|
||||
self.set_write_protection(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_reset_reason(cpu: Cpu) -> Option<SocResetReason> {
|
||||
let reason = unsafe { rtc_get_reset_reason(cpu as u32) };
|
||||
let reason = SocResetReason::from_repr(reason as usize);
|
||||
|
||||
reason
|
||||
}
|
||||
|
||||
pub fn get_wakeup_cause() -> SleepSource {
|
||||
if get_reset_reason(Cpu::ProCpu).unwrap() != SocResetReason::CoreDeepSleep {
|
||||
return SleepSource::Undefined;
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c6, esp32h2))]
|
||||
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
|
||||
(&*crate::peripherals::PMU::PTR)
|
||||
.slp_wakeup_status0
|
||||
.read()
|
||||
.wakeup_cause()
|
||||
.bits()
|
||||
});
|
||||
#[cfg(not(any(esp32, esp32c6, esp32h2)))]
|
||||
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
|
||||
(&*RTC_CNTL::PTR)
|
||||
.slp_wakeup_cause
|
||||
.read()
|
||||
.wakeup_cause()
|
||||
.bits()
|
||||
});
|
||||
#[cfg(esp32)]
|
||||
let wakeup_cause = WakeupReason::from_bits_retain(unsafe {
|
||||
(&*RTC_CNTL::PTR).wakeup_state.read().wakeup_cause().bits() as u32
|
||||
});
|
||||
|
||||
if wakeup_cause.contains(WakeupReason::TimerTrigEn) {
|
||||
return SleepSource::Timer;
|
||||
}
|
||||
if wakeup_cause.contains(WakeupReason::GpioTrigEn) {
|
||||
return SleepSource::Gpio;
|
||||
}
|
||||
if wakeup_cause.intersects(WakeupReason::Uart0TrigEn | WakeupReason::Uart1TrigEn) {
|
||||
return SleepSource::Uart;
|
||||
}
|
||||
|
||||
#[cfg(pm_support_ext0_wakeup)]
|
||||
if wakeup_cause.contains(WakeupReason::ExtEvent0Trig) {
|
||||
return SleepSource::Ext0;
|
||||
}
|
||||
#[cfg(pm_support_ext1_wakeup)]
|
||||
if wakeup_cause.contains(WakeupReason::ExtEvent1Trig) {
|
||||
return SleepSource::Ext1;
|
||||
}
|
||||
|
||||
#[cfg(pm_support_touch_sensor_wakeup)]
|
||||
if wakeup_cause.contains(WakeupReason::TouchTrigEn) {
|
||||
return SleepSource::TouchPad;
|
||||
}
|
||||
|
||||
#[cfg(ulp_supported)]
|
||||
if wakeup_cause.contains(WakeupReason::UlpTrigEn) {
|
||||
return SleepSource::Ulp;
|
||||
}
|
||||
|
||||
#[cfg(pm_support_wifi_wakeup)]
|
||||
if wakeup_cause.contains(WakeupReason::WifiTrigEn) {
|
||||
return SleepSource::Wifi;
|
||||
}
|
||||
|
||||
#[cfg(pm_support_bt_wakeup)]
|
||||
if wakeup_cause.contains(WakeupReason::BtTrigEn) {
|
||||
return SleepSource::BT;
|
||||
}
|
||||
|
||||
#[cfg(riscv_coproc_supported)]
|
||||
if wakeup_cause.contains(WakeupReason::CocpuTrigEn) {
|
||||
return SleepSource::Ulp;
|
||||
} else if wakeup_cause.contains(WakeupReason::CocpuTrapTrigEn) {
|
||||
return SleepSource::CocpuTrapTrig;
|
||||
}
|
||||
|
||||
return SleepSource::Undefined;
|
||||
}
|
||||
@ -1,686 +0,0 @@
|
||||
use fugit::HertzU32;
|
||||
use strum::FromRepr;
|
||||
|
||||
use crate::{
|
||||
clock::{clocks_ll::regi2c_write_mask, Clock, XtalClock},
|
||||
peripherals::{LP_AON, LP_CLKRST, PCR, PMU, TIMG0},
|
||||
soc::efuse::{Efuse, WAFER_VERSION_MAJOR, WAFER_VERSION_MINOR},
|
||||
};
|
||||
|
||||
const I2C_DIG_REG: u8 = 0x6d;
|
||||
const I2C_DIG_REG_HOSTID: u8 = 0;
|
||||
|
||||
const I2C_DIG_REG_XPD_RTC_REG: u8 = 13;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_MSB: u8 = 2;
|
||||
const I2C_DIG_REG_XPD_RTC_REG_LSB: u8 = 2;
|
||||
|
||||
const I2C_DIG_REG_XPD_DIG_REG: u8 = 13;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_MSB: u8 = 3;
|
||||
const I2C_DIG_REG_XPD_DIG_REG_LSB: u8 = 3;
|
||||
|
||||
const I2C_DIG_REG_ENIF_RTC_DREG: u8 = 5;
|
||||
const I2C_DIG_REG_ENIF_RTC_DREG_MSB: u8 = 7;
|
||||
const I2C_DIG_REG_ENIF_RTC_DREG_LSB: u8 = 7;
|
||||
|
||||
const I2C_DIG_REG_ENIF_DIG_DREG: u8 = 7;
|
||||
const I2C_DIG_REG_ENIF_DIG_DREG_MSB: u8 = 7;
|
||||
const I2C_DIG_REG_ENIF_DIG_DREG_LSB: u8 = 7;
|
||||
|
||||
const I2C_DIG_REG_SCK_DCAP: u8 = 14;
|
||||
const I2C_DIG_REG_SCK_DCAP_MSB: u8 = 7;
|
||||
const I2C_DIG_REG_SCK_DCAP_LSB: u8 = 0;
|
||||
|
||||
pub(crate) fn init() {
|
||||
let pmu = unsafe { &*PMU::ptr() };
|
||||
|
||||
pmu.rf_pwc
|
||||
.modify(|_, w| w.perif_i2c_rstb().set_bit().xpd_perif_i2c().set_bit());
|
||||
|
||||
unsafe {
|
||||
regi2c_write_mask(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_HOSTID,
|
||||
I2C_DIG_REG_ENIF_RTC_DREG,
|
||||
I2C_DIG_REG_ENIF_RTC_DREG_MSB,
|
||||
I2C_DIG_REG_ENIF_RTC_DREG_LSB,
|
||||
1,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_HOSTID,
|
||||
I2C_DIG_REG_ENIF_DIG_DREG,
|
||||
I2C_DIG_REG_ENIF_DIG_DREG_MSB,
|
||||
I2C_DIG_REG_ENIF_DIG_DREG_LSB,
|
||||
1,
|
||||
);
|
||||
|
||||
regi2c_write_mask(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_HOSTID,
|
||||
I2C_DIG_REG_XPD_RTC_REG,
|
||||
I2C_DIG_REG_XPD_RTC_REG_MSB,
|
||||
I2C_DIG_REG_XPD_RTC_REG_LSB,
|
||||
0,
|
||||
);
|
||||
regi2c_write_mask(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_HOSTID,
|
||||
I2C_DIG_REG_XPD_DIG_REG,
|
||||
I2C_DIG_REG_XPD_DIG_REG_MSB,
|
||||
I2C_DIG_REG_XPD_DIG_REG_LSB,
|
||||
0,
|
||||
);
|
||||
|
||||
pmu.hp_active_hp_regulator0
|
||||
.modify(|_, w| w.hp_active_hp_regulator_dbias().bits(25));
|
||||
pmu.hp_sleep_lp_regulator0
|
||||
.modify(|_, w| w.hp_sleep_lp_regulator_dbias().bits(26));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clock() {
|
||||
assert!(matches!(
|
||||
RtcClock::get_xtal_freq(),
|
||||
XtalClock::RtcXtalFreq40M
|
||||
));
|
||||
|
||||
RtcClock::set_fast_freq(RtcFastClock::RtcFastClockRcFast);
|
||||
|
||||
let cal_val = loop {
|
||||
RtcClock::set_slow_freq(RtcSlowClock::RtcSlowClockRcSlow);
|
||||
|
||||
let res = RtcClock::calibrate(RtcCalSel::RtcCalRtcMux, 1024);
|
||||
if res != 0 {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let lp_aon = &*LP_AON::ptr();
|
||||
lp_aon.store1.modify(|_, w| w.bits(cal_val));
|
||||
}
|
||||
|
||||
modem_clk_domain_active_state_icg_map_preinit();
|
||||
}
|
||||
|
||||
fn modem_clk_domain_active_state_icg_map_preinit() {
|
||||
unsafe {
|
||||
let pmu = &*PMU::PTR;
|
||||
let lp_clkrst = &*LP_CLKRST::PTR;
|
||||
let pcr = &*PCR::PTR;
|
||||
|
||||
pmu.hp_active_icg_modem
|
||||
.modify(|_, w| w.hp_active_dig_icg_modem_code().bits(2));
|
||||
|
||||
const MODEM_SYSCON_CLK_CONF_POWER_ST: u32 = 0x600A9800 + 0xc;
|
||||
const MODEM_LPCON_CLK_CONF_POWER_ST: u32 = 0x600A9800 + 0x20;
|
||||
|
||||
(MODEM_SYSCON_CLK_CONF_POWER_ST as *mut u32).write_volatile(
|
||||
(MODEM_SYSCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28,
|
||||
);
|
||||
|
||||
(MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).write_volatile(
|
||||
(MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28,
|
||||
);
|
||||
|
||||
(MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).write_volatile(
|
||||
(MODEM_LPCON_CLK_CONF_POWER_ST as *mut u32).read_volatile() & !(3 << 28) | 2 << 28,
|
||||
);
|
||||
|
||||
pmu.imm_modem_icg
|
||||
.write(|w| w.update_dig_icg_modem_en().set_bit());
|
||||
pmu.imm_sleep_sysclk
|
||||
.write(|w| w.update_dig_icg_switch().set_bit());
|
||||
|
||||
lp_clkrst.fosc_cntl.modify(|_, w| w.fosc_dfreq().bits(100));
|
||||
regi2c_write_mask(
|
||||
I2C_DIG_REG,
|
||||
I2C_DIG_REG_HOSTID,
|
||||
I2C_DIG_REG_SCK_DCAP,
|
||||
I2C_DIG_REG_SCK_DCAP_MSB,
|
||||
I2C_DIG_REG_SCK_DCAP_LSB,
|
||||
128,
|
||||
);
|
||||
lp_clkrst
|
||||
.rc32k_cntl
|
||||
.modify(|_, w| w.rc32k_dfreq().bits(100));
|
||||
|
||||
// https://github.com/espressif/esp-idf/commit/e3148369f32fdc6de62c35a67f7adb6f4faef4e3#diff-cc84d279f2f3d77fe252aa40a64d4813f271a52b5a4055e876efd012d888e135R810-R815
|
||||
pcr.ctrl_tick_conf
|
||||
.modify(|_, w| w.fosc_tick_num().bits(255 as u8));
|
||||
}
|
||||
}
|
||||
|
||||
// Terminology:
|
||||
//
|
||||
// CPU Reset: Reset CPU core only, once reset done, CPU will execute from
|
||||
// reset vector
|
||||
// Core Reset: Reset the whole digital system except RTC sub-system
|
||||
// System Reset: Reset the whole digital system, including RTC sub-system
|
||||
// Chip Reset: Reset the whole chip, including the analog part
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
|
||||
pub enum SocResetReason {
|
||||
/// Power on reset
|
||||
///
|
||||
/// In ESP-IDF this value (0x01) can *also* be `ChipBrownOut` or
|
||||
/// `ChipSuperWdt`, however that is not really compatible with Rust-style
|
||||
/// enums.
|
||||
ChipPowerOn = 0x01,
|
||||
/// Software resets the digital core by RTC_CNTL_SW_SYS_RST
|
||||
CoreSw = 0x03,
|
||||
/// Deep sleep reset the digital core
|
||||
CoreDeepSleep = 0x05,
|
||||
/// SDIO Core reset
|
||||
CoreSDIO = 0x06,
|
||||
/// Main watch dog 0 resets digital core
|
||||
CoreMwdt0 = 0x07,
|
||||
/// Main watch dog 1 resets digital core
|
||||
CoreMwdt1 = 0x08,
|
||||
/// RTC watch dog resets digital core
|
||||
CoreRtcWdt = 0x09,
|
||||
/// Main watch dog 0 resets CPU 0
|
||||
Cpu0Mwdt0 = 0x0B,
|
||||
/// Software resets CPU 0 by RTC_CNTL_SW_PROCPU_RST
|
||||
Cpu0Sw = 0x0C,
|
||||
/// RTC watch dog resets CPU 0
|
||||
Cpu0RtcWdt = 0x0D,
|
||||
/// VDD voltage is not stable and resets the digital core
|
||||
SysBrownOut = 0x0F,
|
||||
/// RTC watch dog resets digital core and rtc module
|
||||
SysRtcWdt = 0x10,
|
||||
/// Main watch dog 1 resets CPU 0
|
||||
Cpu0Mwdt1 = 0x11,
|
||||
/// Super watch dog resets the digital core and rtc module
|
||||
SysSuperWdt = 0x12,
|
||||
/// eFuse CRC error resets the digital core
|
||||
CoreEfuseCrc = 0x14,
|
||||
/// USB UART resets the digital core
|
||||
CoreUsbUart = 0x15,
|
||||
/// USB JTAG resets the digital core
|
||||
CoreUsbJtag = 0x16,
|
||||
/// JTAG resets CPU
|
||||
Cpu0JtagCpu = 0x18,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn ets_delay_us(us: u32);
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// RTC SLOW_CLK frequency values
|
||||
pub(crate) enum RtcFastClock {
|
||||
/// Select RC_FAST_CLK as RTC_FAST_CLK source
|
||||
RtcFastClockRcFast = 0,
|
||||
/// Select XTAL_D2_CLK as RTC_FAST_CLK source
|
||||
RtcFastClockXtalD2 = 1,
|
||||
}
|
||||
|
||||
impl Clock for RtcFastClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
RtcFastClock::RtcFastClockXtalD2 => HertzU32::Hz(40_000_000 / 2), /* TODO: Is the value correct? */
|
||||
RtcFastClock::RtcFastClockRcFast => HertzU32::Hz(17_500_000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// RTC SLOW_CLK frequency values
|
||||
pub(crate) enum RtcSlowClock {
|
||||
/// Select RC_SLOW_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClockRcSlow = 0,
|
||||
/// Select XTAL32K_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClock32kXtal = 1,
|
||||
/// Select RC32K_CLK as RTC_SLOW_CLK source
|
||||
RtcSlowClock32kRc = 2,
|
||||
/// Select OSC_SLOW_CLK (external slow clock) as RTC_SLOW_CLK source
|
||||
RtcSlowOscSlow = 3,
|
||||
}
|
||||
|
||||
impl Clock for RtcSlowClock {
|
||||
fn frequency(&self) -> HertzU32 {
|
||||
match self {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => HertzU32::Hz(136_000),
|
||||
RtcSlowClock::RtcSlowClock32kXtal => HertzU32::Hz(32_768),
|
||||
RtcSlowClock::RtcSlowClock32kRc => HertzU32::Hz(32_768),
|
||||
RtcSlowClock::RtcSlowOscSlow => HertzU32::Hz(32_768),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// Clock source to be calibrated using rtc_clk_cal function
|
||||
pub(crate) enum RtcCalSel {
|
||||
/// Currently selected RTC SLOW_CLK
|
||||
RtcCalRtcMux = -1,
|
||||
/// Internal 150kHz RC oscillator
|
||||
RtcCalRcSlow = 0,
|
||||
/// External 32kHz XTAL, as one type of 32k clock
|
||||
RtcCal32kXtal = 1,
|
||||
/// Internal 32kHz RC oscillator, as one type of 32k clock
|
||||
RtcCal32kRc = 2,
|
||||
/// External slow clock signal input by lp_pad_gpio0, as one type of 32k
|
||||
/// clock
|
||||
RtcCal32kOscSlow = 3,
|
||||
/// Internal 20MHz RC oscillator
|
||||
RtcCalRcFast,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum RtcCaliClkSel {
|
||||
CaliClkRcSlow = 0,
|
||||
CaliClkRcFast = 1,
|
||||
CaliClk32k = 2,
|
||||
}
|
||||
/// RTC Watchdog Timer
|
||||
pub struct RtcClock;
|
||||
|
||||
/// RTC Watchdog Timer driver
|
||||
impl RtcClock {
|
||||
const CAL_FRACT: u32 = 19;
|
||||
|
||||
/// Get main XTAL frequency
|
||||
/// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the
|
||||
/// bootloader, as passed to rtc_clk_init function.
|
||||
fn get_xtal_freq() -> XtalClock {
|
||||
let xtal_freq_reg = unsafe { &*LP_AON::PTR }.store4.read().bits();
|
||||
|
||||
// Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in
|
||||
// lower and upper 16-bit halves. These are the routines to work with such a
|
||||
// representation.
|
||||
let clk_val_is_valid = |val| {
|
||||
(val & 0xffffu32) == ((val >> 16u32) & 0xffffu32) && val != 0u32 && val != u32::MAX
|
||||
};
|
||||
let reg_val_to_clk_val = |val| val & u16::MAX as u32;
|
||||
|
||||
if !clk_val_is_valid(xtal_freq_reg) {
|
||||
return XtalClock::RtcXtalFreq40M;
|
||||
}
|
||||
|
||||
match reg_val_to_clk_val(xtal_freq_reg) {
|
||||
40 => XtalClock::RtcXtalFreq40M,
|
||||
other => XtalClock::RtcXtalFreqOther(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the RTC_SLOW_CLK source
|
||||
pub(crate) fn get_slow_freq() -> RtcSlowClock {
|
||||
let lp_clrst = unsafe { &*LP_CLKRST::ptr() };
|
||||
|
||||
let slow_freq = lp_clrst.lp_clk_conf.read().slow_clk_sel().bits();
|
||||
match slow_freq {
|
||||
0 => RtcSlowClock::RtcSlowClockRcSlow,
|
||||
1 => RtcSlowClock::RtcSlowClock32kXtal,
|
||||
2 => RtcSlowClock::RtcSlowClock32kRc,
|
||||
3 => RtcSlowClock::RtcSlowOscSlow,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_slow_freq(slow_freq: RtcSlowClock) {
|
||||
unsafe {
|
||||
let lp_clkrst = &*LP_CLKRST::PTR;
|
||||
|
||||
lp_clkrst
|
||||
.lp_clk_conf
|
||||
.modify(|_, w| w.slow_clk_sel().bits(slow_freq as u8));
|
||||
lp_clkrst.clk_to_hp.modify(|_, w| {
|
||||
w.icg_hp_xtal32k()
|
||||
.bit(match slow_freq {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => true,
|
||||
_ => false,
|
||||
})
|
||||
.icg_hp_xtal32k()
|
||||
.bit(match slow_freq {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => true,
|
||||
_ => false,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: IDF-5781 Some of esp32c6 SOC_RTC_FAST_CLK_SRC_XTAL_D2 rtc_fast clock
|
||||
// has timing issue Force to use SOC_RTC_FAST_CLK_SRC_RC_FAST since 2nd
|
||||
// stage bootloader https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/src/bootloader_clock_init.c#L65-L67
|
||||
fn set_fast_freq(fast_freq: RtcFastClock) {
|
||||
unsafe {
|
||||
let lp_clkrst = &*LP_CLKRST::PTR;
|
||||
lp_clkrst.lp_clk_conf.modify(|_, w| {
|
||||
w.fast_clk_sel().bit(match fast_freq {
|
||||
RtcFastClock::RtcFastClockRcFast => false,
|
||||
RtcFastClock::RtcFastClockXtalD2 => true,
|
||||
})
|
||||
});
|
||||
ets_delay_us(3);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibration of RTC_SLOW_CLK is performed using a special feature of
|
||||
/// TIMG0. This feature counts the number of XTAL clock cycles within a
|
||||
/// given number of RTC_SLOW_CLK cycles.
|
||||
fn calibrate_internal(mut cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
const SOC_CLK_RC_FAST_FREQ_APPROX: u32 = 17_500_000;
|
||||
const SOC_CLK_RC_SLOW_FREQ_APPROX: u32 = 136_000;
|
||||
const SOC_CLK_XTAL32K_FREQ_APPROX: u32 = 32768;
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match cal_clk {
|
||||
RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() {
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
_ => cal_clk,
|
||||
},
|
||||
RtcCalSel::RtcCal32kOscSlow => RtcCalSel::RtcCalRtcMux,
|
||||
_ => cal_clk,
|
||||
};
|
||||
}
|
||||
|
||||
let lp_clkrst = unsafe { &*LP_CLKRST::ptr() };
|
||||
let pcr = unsafe { &*PCR::ptr() };
|
||||
let pmu = unsafe { &*PMU::ptr() };
|
||||
|
||||
let clk_src = RtcClock::get_slow_freq();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match clk_src {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow,
|
||||
};
|
||||
}
|
||||
|
||||
let cali_clk_sel;
|
||||
if cal_clk == RtcCalSel::RtcCalRtcMux {
|
||||
cal_clk = match clk_src {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRcSlow,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCalRcSlow,
|
||||
}
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClkRcFast;
|
||||
} else if cal_clk == RtcCalSel::RtcCalRcSlow {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClkRcSlow;
|
||||
} else {
|
||||
cali_clk_sel = RtcCaliClkSel::CaliClk32k;
|
||||
match cal_clk {
|
||||
RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => (),
|
||||
RtcCalSel::RtcCal32kRc => pcr
|
||||
.ctrl_32k_conf
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }),
|
||||
RtcCalSel::RtcCal32kXtal => pcr
|
||||
.ctrl_32k_conf
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }),
|
||||
RtcCalSel::RtcCal32kOscSlow => pcr
|
||||
.ctrl_32k_conf
|
||||
.modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }),
|
||||
}
|
||||
}
|
||||
|
||||
// Enable requested clock (150k is always on)
|
||||
// Some delay is required before the time is stable
|
||||
// Only enable if originaly was disabled
|
||||
// If clock is already on, do nothing
|
||||
|
||||
let dig_32k_xtal_enabled = lp_clkrst.clk_to_hp.read().icg_hp_xtal32k().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp
|
||||
.modify(|_, w| w.icg_hp_xtal32k().set_bit());
|
||||
}
|
||||
|
||||
// TODO: very hacky
|
||||
// in ESP-IDF these are not called in this function but the fields are set
|
||||
lp_clkrst
|
||||
.clk_to_hp
|
||||
.modify(|_, w| w.icg_hp_xtal32k().set_bit());
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_xtal32k().set_bit());
|
||||
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
|
||||
let rc_fast_enabled = pmu
|
||||
.hp_sleep_lp_ck_power
|
||||
.read()
|
||||
.hp_sleep_xpd_fosc_clk()
|
||||
.bit_is_set();
|
||||
let dig_rc_fast_enabled = lp_clkrst.clk_to_hp.read().icg_hp_fosc().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
if !rc_fast_enabled {
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(50);
|
||||
}
|
||||
}
|
||||
|
||||
if !dig_rc_fast_enabled {
|
||||
lp_clkrst.clk_to_hp.modify(|_, w| w.icg_hp_fosc().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rc32k_enabled = pmu
|
||||
.hp_sleep_lp_ck_power
|
||||
.read()
|
||||
.hp_sleep_xpd_rc32k()
|
||||
.bit_is_set();
|
||||
let dig_rc32k_enabled = lp_clkrst.clk_to_hp.read().icg_hp_osc32k().bit_is_set();
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kRc {
|
||||
if !rc32k_enabled {
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(300);
|
||||
}
|
||||
}
|
||||
|
||||
if !dig_rc32k_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp
|
||||
.modify(|_, w| w.icg_hp_osc32k().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is already running calibration process
|
||||
// TODO: &mut TIMG0 for calibration
|
||||
let timg0 = unsafe { &*TIMG0::ptr() };
|
||||
|
||||
if timg0
|
||||
.rtccalicfg
|
||||
.read()
|
||||
.rtc_cali_start_cycling()
|
||||
.bit_is_set()
|
||||
{
|
||||
timg0
|
||||
.rtccalicfg2
|
||||
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(1) });
|
||||
|
||||
// Set small timeout threshold to accelerate the generation of timeot
|
||||
// Internal circuit will be reset when timeout occurs and will not affect the
|
||||
// next calibration
|
||||
while !timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_set()
|
||||
&& !timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_set()
|
||||
{}
|
||||
}
|
||||
|
||||
// Prepare calibration
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| unsafe { w.rtc_cali_clk_sel().bits(cali_clk_sel.clone() as u8) });
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| w.rtc_cali_start_cycling().clear_bit());
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| unsafe { w.rtc_cali_max().bits(slowclk_cycles as u16) });
|
||||
|
||||
let expected_freq = match cali_clk_sel {
|
||||
RtcCaliClkSel::CaliClk32k => {
|
||||
timg0.rtccalicfg2.modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 12)
|
||||
});
|
||||
SOC_CLK_XTAL32K_FREQ_APPROX
|
||||
}
|
||||
RtcCaliClkSel::CaliClkRcFast => {
|
||||
timg0
|
||||
.rtccalicfg2
|
||||
.modify(|_, w| unsafe { w.rtc_cali_timeout_thres().bits(0x01FFFFFF) });
|
||||
SOC_CLK_RC_FAST_FREQ_APPROX
|
||||
}
|
||||
_ => {
|
||||
timg0.rtccalicfg2.modify(|_, w| unsafe {
|
||||
w.rtc_cali_timeout_thres().bits(slowclk_cycles << 10)
|
||||
});
|
||||
SOC_CLK_RC_SLOW_FREQ_APPROX
|
||||
}
|
||||
};
|
||||
|
||||
let us_time_estimate = (HertzU32::MHz(slowclk_cycles) / expected_freq).to_Hz();
|
||||
|
||||
// Start calibration
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit());
|
||||
timg0.rtccalicfg.modify(|_, w| w.rtc_cali_start().set_bit());
|
||||
|
||||
// Wait for calibration to finish up to another us_time_estimate
|
||||
unsafe {
|
||||
ets_delay_us(us_time_estimate);
|
||||
}
|
||||
|
||||
let cal_val = loop {
|
||||
if timg0.rtccalicfg.read().rtc_cali_rdy().bit_is_set() {
|
||||
let minor: u8 = Efuse::read_field_le(WAFER_VERSION_MINOR);
|
||||
let major: u8 = Efuse::read_field_le(WAFER_VERSION_MAJOR);
|
||||
|
||||
// The Fosc CLK of calibration circuit is divided by 32 for ECO1.
|
||||
// So we need to multiply the frequency of the Fosc for ECO1 and above chips by
|
||||
// 32 times. And ensure that this modification will not affect
|
||||
// ECO0. PS: For ESP32C6 ECO0 chip version is v0.0 only, which
|
||||
// means that both MAJOR and MINOR are 0. The chip version is
|
||||
// calculated using the following formula: MAJOR * 100 + MINOR. (if the result
|
||||
// is 1, then version is v0.1) https://github.com/espressif/esp-idf/commit/e3148369f32fdc6de62c35a67f7adb6f4faef4e3
|
||||
if (major * 100 + minor) > 0 {
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
break timg0.rtccalicfg1.read().rtc_cali_value().bits() >> 5;
|
||||
}
|
||||
break timg0.rtccalicfg1.read().rtc_cali_value().bits();
|
||||
} else {
|
||||
break timg0.rtccalicfg1.read().rtc_cali_value().bits();
|
||||
}
|
||||
}
|
||||
|
||||
if timg0.rtccalicfg2.read().rtc_cali_timeout().bit_is_set() {
|
||||
// Timed out waiting for calibration
|
||||
break 0;
|
||||
}
|
||||
};
|
||||
|
||||
timg0
|
||||
.rtccalicfg
|
||||
.modify(|_, w| w.rtc_cali_start().clear_bit());
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kXtal && !dig_32k_xtal_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp
|
||||
.modify(|_, w| w.icg_hp_xtal32k().clear_bit());
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
if rc_fast_enabled {
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_fosc_clk().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(50);
|
||||
}
|
||||
}
|
||||
|
||||
if dig_rc_fast_enabled {
|
||||
lp_clkrst.clk_to_hp.modify(|_, w| w.icg_hp_fosc().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cal_clk == RtcCalSel::RtcCal32kRc {
|
||||
if rc32k_enabled {
|
||||
pmu.hp_sleep_lp_ck_power
|
||||
.modify(|_, w| w.hp_sleep_xpd_rc32k().set_bit());
|
||||
unsafe {
|
||||
ets_delay_us(300);
|
||||
}
|
||||
}
|
||||
if dig_rc32k_enabled {
|
||||
lp_clkrst
|
||||
.clk_to_hp
|
||||
.modify(|_, w| w.icg_hp_osc32k().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
cal_val
|
||||
}
|
||||
|
||||
/// Measure RTC slow clock's period, based on main XTAL frequency
|
||||
///
|
||||
/// This function will time out and return 0 if the time for the given
|
||||
/// number of cycles to be counted exceeds the expected time twice. This
|
||||
/// may happen if 32k XTAL is being calibrated, but the oscillator has
|
||||
/// not started up (due to incorrect loading capacitance, board design
|
||||
/// issue, or lack of 32 XTAL on board).
|
||||
fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 {
|
||||
let xtal_freq = RtcClock::get_xtal_freq();
|
||||
|
||||
let minor: u8 = Efuse::read_field_le(WAFER_VERSION_MINOR);
|
||||
let major: u8 = Efuse::read_field_le(WAFER_VERSION_MAJOR);
|
||||
|
||||
// The Fosc CLK of calibration circuit is divided by 32 for ECO1.
|
||||
// So we need to divide the calibrate cycles of the FOSC for ECO1 and above
|
||||
// chips by 32 to avoid excessive calibration time.*/
|
||||
// PS: For ESP32C6 ECO0 chip version is v0.0 only, which means that both MAJOR
|
||||
// and MINOR are 0. The chip version is calculated using the following
|
||||
// formula: MAJOR * 100 + MINOR. (if the result is 1, then version is v0.1)
|
||||
if (major * 100 + minor) > 0 {
|
||||
if cal_clk == RtcCalSel::RtcCalRcFast {
|
||||
let slowclk_cycles = slowclk_cycles >> 5;
|
||||
}
|
||||
}
|
||||
|
||||
let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64;
|
||||
let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64;
|
||||
let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider;
|
||||
|
||||
(period_64 & u32::MAX as u64) as u32
|
||||
}
|
||||
|
||||
/// Calculate the necessary RTC_SLOW_CLK cycles to complete 1 millisecond.
|
||||
pub(crate) fn cycles_to_1ms() -> u16 {
|
||||
let period_13q19 = RtcClock::calibrate(
|
||||
match RtcClock::get_slow_freq() {
|
||||
RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRtcMux,
|
||||
RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal,
|
||||
RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc,
|
||||
RtcSlowClock::RtcSlowOscSlow => RtcCalSel::RtcCal32kOscSlow,
|
||||
// RtcSlowClock::RtcCalRcFast => RtcCalSel::RtcCalRcFast,
|
||||
},
|
||||
1024,
|
||||
);
|
||||
|
||||
// 100_000_000 is used to get rid of `float` calculations
|
||||
let period = (100_000_000 * period_13q19 as u64) / (1 << RtcClock::CAL_FRACT);
|
||||
|
||||
(100_000_000 * 1000 / period) as u16
|
||||
}
|
||||
}
|
||||
@ -1,521 +0,0 @@
|
||||
use core::convert::Infallible;
|
||||
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals::SHA,
|
||||
system::PeripheralClockControl,
|
||||
};
|
||||
|
||||
// All the hash algorithms introduced in FIPS PUB 180-4 Spec.
|
||||
// – SHA-1
|
||||
// – SHA-224
|
||||
// – SHA-256
|
||||
// – SHA-384
|
||||
// – SHA-512
|
||||
// – SHA-512/224
|
||||
// – SHA-512/256
|
||||
// – SHA-512/t (not implemented yet)
|
||||
// Two working modes
|
||||
// – Typical SHA
|
||||
// – DMA-SHA (not implemented yet)
|
||||
|
||||
const ALIGN_SIZE: usize = core::mem::size_of::<u32>();
|
||||
|
||||
// ESP32 does reversed order
|
||||
#[cfg(esp32)]
|
||||
const U32_FROM_BYTES: fn([u8; 4]) -> u32 = u32::from_be_bytes;
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
const U32_FROM_BYTES: fn([u8; 4]) -> u32 = u32::from_ne_bytes;
|
||||
|
||||
// The alignment helper helps you write to registers that only accepts u32 using
|
||||
// regular u8s (bytes) It keeps a write buffer of 4 u8 (could in theory be 3 but
|
||||
// less convient) And if the incoming data is not convertable to u32 (i.e. not a
|
||||
// multiple of 4 in length) it will store the remainder in the buffer until the
|
||||
// next call
|
||||
//
|
||||
// It assumes incoming `dst` are aligned to desired layout (in future
|
||||
// ptr.is_aligned can be used) It also assumes that writes are done in FIFO
|
||||
// order
|
||||
#[derive(Debug)]
|
||||
struct AlignmentHelper {
|
||||
buf: [u8; ALIGN_SIZE],
|
||||
buf_fill: usize,
|
||||
}
|
||||
|
||||
impl AlignmentHelper {
|
||||
pub fn default() -> AlignmentHelper {
|
||||
AlignmentHelper {
|
||||
buf: [0u8; ALIGN_SIZE],
|
||||
buf_fill: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// This function will write any remaining buffer to dst and return the amount of
|
||||
// *bytes* written (0 means no write)
|
||||
pub unsafe fn flush_to(&mut self, dst: *mut u32) -> usize {
|
||||
if self.buf_fill != 0 {
|
||||
for i in self.buf_fill..ALIGN_SIZE {
|
||||
self.buf[i] = 0;
|
||||
}
|
||||
|
||||
dst.write_volatile(U32_FROM_BYTES(self.buf));
|
||||
}
|
||||
|
||||
let flushed = self.buf_fill;
|
||||
self.buf_fill = 0;
|
||||
|
||||
return flushed;
|
||||
}
|
||||
|
||||
// This function is similar to `volatile_set_memory` but will prepend data that
|
||||
// was previously ingested and ensure aligned (u32) writes
|
||||
#[allow(unused)]
|
||||
pub unsafe fn volatile_write_bytes(&mut self, dst: *mut u32, val: u8, count: usize) {
|
||||
let mut cursor = 0;
|
||||
if self.buf_fill != 0 {
|
||||
for i in self.buf_fill..ALIGN_SIZE {
|
||||
self.buf[i] = val;
|
||||
}
|
||||
|
||||
dst.write_volatile(U32_FROM_BYTES(self.buf));
|
||||
cursor = 1;
|
||||
|
||||
self.buf_fill = 0;
|
||||
}
|
||||
|
||||
core::ptr::write_bytes(dst.add(cursor), val, count);
|
||||
}
|
||||
|
||||
// This function is similar to `volatile_copy_nonoverlapping_memory`, however it
|
||||
// buffers up to a u32 in order to always write to registers in an aligned
|
||||
// way. Additionally it will keep stop writing when the end of the register
|
||||
// (defined by `dst_bound` relative to `dst`) and returns the remaining data
|
||||
// (if not possible to write everything), and if it wrote till dst_bound or
|
||||
// exited early (due to lack of data).
|
||||
pub unsafe fn aligned_volatile_copy<'a>(
|
||||
&mut self,
|
||||
dst: *mut u32,
|
||||
src: &'a [u8],
|
||||
dst_bound: usize,
|
||||
) -> (&'a [u8], bool) {
|
||||
assert!(dst_bound > 0);
|
||||
|
||||
let mut nsrc = src;
|
||||
let mut cursor = 0;
|
||||
if self.buf_fill != 0 {
|
||||
// First prepend existing data
|
||||
let max_fill = ALIGN_SIZE - self.buf_fill;
|
||||
let (nbuf, src) = src.split_at(core::cmp::min(src.len(), max_fill));
|
||||
nsrc = src;
|
||||
for i in 0..max_fill {
|
||||
match nbuf.get(i) {
|
||||
Some(v) => {
|
||||
self.buf[self.buf_fill + i] = *v;
|
||||
self.buf_fill += 1;
|
||||
}
|
||||
None => return (&[], false), // Used up entire buffer before filling buff_fil
|
||||
}
|
||||
}
|
||||
|
||||
dst.write_volatile(U32_FROM_BYTES(self.buf));
|
||||
cursor += 1;
|
||||
|
||||
self.buf_fill = 0;
|
||||
}
|
||||
|
||||
if dst_bound <= cursor * ALIGN_SIZE {
|
||||
return (nsrc, true);
|
||||
}
|
||||
|
||||
let (to_write, remaining) = nsrc.split_at(core::cmp::min(
|
||||
dst_bound - cursor * ALIGN_SIZE,
|
||||
(nsrc.len() / ALIGN_SIZE) * ALIGN_SIZE, // TODO: unstable div_floor for clarity?
|
||||
));
|
||||
|
||||
if to_write.len() > 0 {
|
||||
// Raw v_c_n_m also works but only when src.len() >= 4 * ALIGN_SIZE, otherwise
|
||||
// it be broken
|
||||
// core::intrinsics::volatile_copy_nonoverlapping_memory::<u32>(dst.add(cursor),
|
||||
// to_write.as_ptr() as *const u32, to_write.len()/alignment);
|
||||
for (i, v) in to_write.chunks_exact(ALIGN_SIZE).enumerate() {
|
||||
dst.add(i)
|
||||
.write_volatile(U32_FROM_BYTES(v.try_into().unwrap()).to_be());
|
||||
}
|
||||
}
|
||||
|
||||
// If it's data we can't store we don't need to try and align it, just wait for
|
||||
// next write Generally this applies when (src/4*4) != src
|
||||
let was_bounded = dst_bound - to_write.len() == 0;
|
||||
if remaining.len() > 0 && remaining.len() < 4 {
|
||||
for i in 0..remaining.len() {
|
||||
self.buf[i] = remaining[i];
|
||||
}
|
||||
|
||||
self.buf_fill = remaining.len();
|
||||
|
||||
return (&[], was_bounded);
|
||||
}
|
||||
|
||||
return (remaining, was_bounded);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sha<'d> {
|
||||
sha: PeripheralRef<'d, SHA>,
|
||||
mode: ShaMode,
|
||||
alignment_helper: AlignmentHelper,
|
||||
cursor: usize,
|
||||
first_run: bool,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ShaMode {
|
||||
SHA1,
|
||||
#[cfg(not(esp32))]
|
||||
SHA224,
|
||||
SHA256,
|
||||
#[cfg(any(esp32s2, esp32s3, esp32))]
|
||||
SHA384,
|
||||
#[cfg(any(esp32s2, esp32s3, esp32))]
|
||||
SHA512,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
SHA512_224,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
SHA512_256,
|
||||
// SHA512_(u16) // Max 511
|
||||
}
|
||||
|
||||
// TODO: Maybe make Sha Generic (Sha<Mode>) in order to allow for better
|
||||
// compiler optimizations? (Requires complex const generics which isn't stable
|
||||
// yet)
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
fn mode_as_bits(mode: ShaMode) -> u8 {
|
||||
match mode {
|
||||
ShaMode::SHA1 => 0,
|
||||
ShaMode::SHA224 => 1,
|
||||
ShaMode::SHA256 => 2,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA384 => 3,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA512 => 4,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA512_224 => 5,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA512_256 => 6,
|
||||
// _ => 0 // TODO: SHA512/t
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Allow/Implemenet SHA512_(u16)
|
||||
|
||||
// A few notes on this implementation with regards to 'memcpy',
|
||||
// - It seems that ptr::write_bytes already acts as volatile, while ptr::copy_*
|
||||
// does not (in this case)
|
||||
// - The registers are *not* cleared after processing, so padding needs to be
|
||||
// written out
|
||||
// - This component uses core::intrinsics::volatile_* which is unstable, but is
|
||||
// the only way to
|
||||
// efficiently copy memory with volatile
|
||||
// - For this particular registers (and probably others), a full u32 needs to be
|
||||
// written partial
|
||||
// register writes (i.e. in u8 mode) does not work
|
||||
// - This means that we need to buffer bytes coming in up to 4 u8's in order
|
||||
// to create a full u32
|
||||
|
||||
// This implementation might fail after u32::MAX/8 bytes, to increase please see
|
||||
// ::finish() length/self.cursor usage
|
||||
impl<'d> Sha<'d> {
|
||||
pub fn new(
|
||||
sha: impl Peripheral<P = SHA> + 'd,
|
||||
mode: ShaMode,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(sha);
|
||||
peripheral_clock_control.enable(crate::system::Peripheral::Sha);
|
||||
|
||||
// Setup SHA Mode
|
||||
#[cfg(not(esp32))]
|
||||
sha.mode
|
||||
.write(|w| unsafe { w.mode().bits(mode_as_bits(mode)) });
|
||||
|
||||
Self {
|
||||
sha,
|
||||
mode,
|
||||
cursor: 0,
|
||||
first_run: true,
|
||||
finished: false,
|
||||
alignment_helper: AlignmentHelper::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first_run(&self) -> bool {
|
||||
self.first_run
|
||||
}
|
||||
|
||||
pub fn finished(&self) -> bool {
|
||||
self.finished
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
fn process_buffer(&mut self) {
|
||||
// FIXME: SHA_START_REG & SHA_CONTINUE_REG are wrongly marked as RO (they are
|
||||
// WO)
|
||||
if self.first_run {
|
||||
// Set SHA_START_REG
|
||||
unsafe {
|
||||
self.sha.start.as_ptr().write_volatile(1u32);
|
||||
}
|
||||
self.first_run = false;
|
||||
} else {
|
||||
// SET SHA_CONTINUE_REG
|
||||
unsafe {
|
||||
self.sha.continue_.as_ptr().write_volatile(1u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
fn process_buffer(&mut self) {
|
||||
if self.first_run {
|
||||
match self.mode {
|
||||
ShaMode::SHA1 => self.sha.sha1_start.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA256 => self.sha.sha256_start.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA384 => self.sha.sha384_start.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA512 => self.sha.sha512_start.write(|w| unsafe { w.bits(1) }),
|
||||
}
|
||||
self.first_run = false;
|
||||
} else {
|
||||
match self.mode {
|
||||
ShaMode::SHA1 => self.sha.sha1_continue.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA256 => self.sha.sha256_continue.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA384 => self.sha.sha384_continue.write(|w| unsafe { w.bits(1) }),
|
||||
ShaMode::SHA512 => self.sha.sha512_continue.write(|w| unsafe { w.bits(1) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chunk_length(&self) -> usize {
|
||||
return match self.mode {
|
||||
ShaMode::SHA1 | ShaMode::SHA256 => 64,
|
||||
#[cfg(not(esp32))]
|
||||
ShaMode::SHA224 => 64,
|
||||
#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))]
|
||||
_ => 128,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
fn is_busy(&self) -> bool {
|
||||
match self.mode {
|
||||
ShaMode::SHA1 => self.sha.sha1_busy.read().sha1_busy().bit_is_set(),
|
||||
ShaMode::SHA256 => self.sha.sha256_busy.read().sha256_busy().bit_is_set(),
|
||||
ShaMode::SHA384 => self.sha.sha384_busy.read().sha384_busy().bit_is_set(),
|
||||
ShaMode::SHA512 => self.sha.sha512_busy.read().sha512_busy().bit_is_set(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
fn is_busy(&self) -> bool {
|
||||
self.sha.busy.read().bits() != 0
|
||||
}
|
||||
|
||||
pub fn digest_length(&self) -> usize {
|
||||
match self.mode {
|
||||
ShaMode::SHA1 => 20,
|
||||
#[cfg(not(esp32))]
|
||||
ShaMode::SHA224 => 28,
|
||||
ShaMode::SHA256 => 32,
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
ShaMode::SHA384 => 48,
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
ShaMode::SHA512 => 64,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA512_224 => 28,
|
||||
#[cfg(any(esp32s2, esp32s3))]
|
||||
ShaMode::SHA512_256 => 32,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
fn input_ptr(&self) -> *mut u32 {
|
||||
return self.sha.m_mem[0].as_ptr() as *mut u32;
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
fn input_ptr(&self) -> *mut u32 {
|
||||
return self.sha.text[0].as_ptr() as *mut u32;
|
||||
}
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
fn output_ptr(&self) -> *const u32 {
|
||||
return self.sha.h_mem[0].as_ptr() as *const u32;
|
||||
}
|
||||
|
||||
#[cfg(esp32)]
|
||||
fn output_ptr(&self) -> *const u32 {
|
||||
return self.sha.text[0].as_ptr() as *const u32;
|
||||
}
|
||||
|
||||
fn flush_data(&mut self) -> nb::Result<(), Infallible> {
|
||||
if self.is_busy() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let dst_ptr = self
|
||||
.input_ptr()
|
||||
.add((self.cursor % self.chunk_length()) / ALIGN_SIZE);
|
||||
let flushed = self.alignment_helper.flush_to(dst_ptr);
|
||||
if flushed != 0 {
|
||||
self.cursor = self.cursor.wrapping_add(ALIGN_SIZE - flushed);
|
||||
if self.cursor % self.chunk_length() == 0 {
|
||||
self.process_buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This function ensures that incoming data is aligned to u32 (due to issues
|
||||
// with cpy_mem<u8>)
|
||||
fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
|
||||
let mod_cursor = self.cursor % self.chunk_length();
|
||||
|
||||
unsafe {
|
||||
let ptr = self.input_ptr().add(mod_cursor / ALIGN_SIZE);
|
||||
let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy(
|
||||
ptr,
|
||||
incoming,
|
||||
self.chunk_length() - mod_cursor,
|
||||
);
|
||||
self.cursor = self.cursor.wrapping_add(incoming.len() - remaining.len());
|
||||
if bound_reached {
|
||||
self.process_buffer();
|
||||
}
|
||||
|
||||
Ok(remaining)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update<'a>(&mut self, buffer: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
|
||||
if self.is_busy() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
self.finished = false;
|
||||
|
||||
let remaining = self.write_data(buffer)?;
|
||||
|
||||
Ok(remaining)
|
||||
}
|
||||
|
||||
// Finish of the calculation (if not alreaedy) and copy result to output
|
||||
// After `finish()` is called `update()`s will contribute to a new hash which
|
||||
// can be calculated again with `finish()`.
|
||||
//
|
||||
// Typically output is expected to be the size of digest_length(), but smaller
|
||||
// inputs can be given to get a "short hash"
|
||||
pub fn finish(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> {
|
||||
// The main purpose of this function is to dynamically generate padding for the
|
||||
// input. Padding: Append "1" bit, Pad zeros until 512/1024 filled
|
||||
// then set the message length in the LSB (overwriting the padding)
|
||||
// If not enough free space for length+1, add length at end of a new zero'd
|
||||
// block
|
||||
|
||||
if self.is_busy() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
let chunk_len = self.chunk_length();
|
||||
|
||||
if !self.finished {
|
||||
// Store message length for padding
|
||||
let length = self.cursor * 8;
|
||||
nb::block!(self.update(&[0x80]))?; // Append "1" bit
|
||||
nb::block!(self.flush_data())?; // Flush partial data, ensures aligned cursor
|
||||
debug_assert!(self.cursor % 4 == 0);
|
||||
|
||||
let mod_cursor = self.cursor % chunk_len;
|
||||
if chunk_len - mod_cursor < chunk_len / 8 {
|
||||
// Zero out remaining data if buffer is almost full (>=448/896), and process
|
||||
// buffer
|
||||
let pad_len = chunk_len - mod_cursor;
|
||||
unsafe {
|
||||
let m_cursor_ptr = self.input_ptr().add(mod_cursor / ALIGN_SIZE);
|
||||
self.alignment_helper.volatile_write_bytes(
|
||||
m_cursor_ptr,
|
||||
0,
|
||||
pad_len / ALIGN_SIZE,
|
||||
);
|
||||
}
|
||||
self.process_buffer();
|
||||
self.cursor = self.cursor.wrapping_add(pad_len);
|
||||
|
||||
// Spin-wait for finish
|
||||
while self.is_busy() {}
|
||||
}
|
||||
|
||||
let mod_cursor = self.cursor % chunk_len; // Should be zero if branched above
|
||||
unsafe {
|
||||
let m_cursor_ptr = self.input_ptr();
|
||||
// Pad zeros
|
||||
let pad_ptr = m_cursor_ptr.add(mod_cursor / ALIGN_SIZE);
|
||||
let pad_len = (chunk_len - mod_cursor) - ALIGN_SIZE;
|
||||
|
||||
self.alignment_helper
|
||||
.volatile_write_bytes(pad_ptr, 0, pad_len / ALIGN_SIZE);
|
||||
|
||||
// Write length (BE) to end
|
||||
// NOTE: aligned_volatile_copy does not work here
|
||||
// The decompiler suggest volatile_copy_memory/write_volatile is optimized to a
|
||||
// simple *v = *pv; While the aligned_volatile_copy makes an
|
||||
// actual call to memcpy, why this makes a difference when
|
||||
// memcpy does works in other places, I don't know
|
||||
let end_ptr = m_cursor_ptr.add((chunk_len / ALIGN_SIZE) - 1);
|
||||
#[cfg(not(esp32))]
|
||||
end_ptr.write_volatile(length.to_be() as u32);
|
||||
#[cfg(esp32)]
|
||||
end_ptr.write_volatile(length.to_le() as u32);
|
||||
}
|
||||
|
||||
self.process_buffer();
|
||||
// Spin-wait for final buffer to be processed
|
||||
while self.is_busy() {}
|
||||
|
||||
// ESP32 requires additional load to retrieve output
|
||||
#[cfg(esp32)]
|
||||
{
|
||||
match self.mode {
|
||||
ShaMode::SHA1 => unsafe { self.sha.sha1_load.write(|w| w.bits(1)) },
|
||||
ShaMode::SHA256 => unsafe { self.sha.sha256_load.write(|w| w.bits(1)) },
|
||||
ShaMode::SHA384 => unsafe { self.sha.sha384_load.write(|w| w.bits(1)) },
|
||||
ShaMode::SHA512 => unsafe { self.sha.sha512_load.write(|w| w.bits(1)) },
|
||||
}
|
||||
|
||||
// Spin wait for result, 8-20 clock cycles according to manual
|
||||
while self.is_busy() {}
|
||||
}
|
||||
|
||||
self.finished = true;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let digest_ptr = self.output_ptr();
|
||||
let out_ptr = output.as_mut_ptr() as *mut u32;
|
||||
let digest_out = core::cmp::min(self.digest_length(), output.len()) / ALIGN_SIZE;
|
||||
for i in 0..digest_out {
|
||||
#[cfg(not(esp32))]
|
||||
out_ptr.add(i).write(*digest_ptr.add(i));
|
||||
// ESP32 does reversed order
|
||||
#[cfg(esp32)]
|
||||
out_ptr.add(i).write((*digest_ptr.add(i)).to_be());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,249 +0,0 @@
|
||||
//! Control CPU Cores
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use xtensa_lx::set_stack_pointer;
|
||||
|
||||
use crate::Cpu;
|
||||
|
||||
static mut START_CORE1_FUNCTION: Option<&'static mut (dyn FnMut() + 'static)> = None;
|
||||
|
||||
/// Will park the APP (second) core when dropped
|
||||
#[must_use]
|
||||
pub struct AppCoreGuard<'a> {
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Drop for AppCoreGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
internal_park_core(Cpu::AppCpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
CoreAlreadyRunning,
|
||||
}
|
||||
|
||||
/// Control CPU Cores
|
||||
pub struct CpuControl {
|
||||
_cpu_control: crate::system::CpuControl,
|
||||
}
|
||||
|
||||
unsafe fn internal_park_core(core: Cpu) {
|
||||
let rtc_control = crate::peripherals::RTC_CNTL::PTR;
|
||||
let rtc_control = &*rtc_control;
|
||||
|
||||
match core {
|
||||
Cpu::ProCpu => {
|
||||
rtc_control
|
||||
.sw_cpu_stall
|
||||
.modify(|_, w| w.sw_stall_procpu_c1().bits(0x21));
|
||||
rtc_control
|
||||
.options0
|
||||
.modify(|_, w| w.sw_stall_procpu_c0().bits(0x02));
|
||||
}
|
||||
Cpu::AppCpu => {
|
||||
rtc_control
|
||||
.sw_cpu_stall
|
||||
.modify(|_, w| w.sw_stall_appcpu_c1().bits(0x21));
|
||||
rtc_control
|
||||
.options0
|
||||
.modify(|_, w| w.sw_stall_appcpu_c0().bits(0x02));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuControl {
|
||||
pub fn new(cpu_control: crate::system::CpuControl) -> CpuControl {
|
||||
CpuControl {
|
||||
_cpu_control: cpu_control,
|
||||
}
|
||||
}
|
||||
|
||||
/// Park the given core
|
||||
pub unsafe fn park_core(&mut self, core: Cpu) {
|
||||
internal_park_core(core);
|
||||
}
|
||||
|
||||
/// Unpark the given core
|
||||
pub fn unpark_core(&mut self, core: Cpu) {
|
||||
let rtc_control = crate::peripherals::RTC_CNTL::PTR;
|
||||
let rtc_control = unsafe { &*rtc_control };
|
||||
|
||||
match core {
|
||||
Cpu::ProCpu => {
|
||||
rtc_control
|
||||
.sw_cpu_stall
|
||||
.modify(|_, w| unsafe { w.sw_stall_procpu_c1().bits(0) });
|
||||
rtc_control
|
||||
.options0
|
||||
.modify(|_, w| unsafe { w.sw_stall_procpu_c0().bits(0) });
|
||||
}
|
||||
Cpu::AppCpu => {
|
||||
rtc_control
|
||||
.sw_cpu_stall
|
||||
.modify(|_, w| unsafe { w.sw_stall_appcpu_c1().bits(0) });
|
||||
rtc_control
|
||||
.options0
|
||||
.modify(|_, w| unsafe { w.sw_stall_appcpu_c0().bits(0) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_cache(&mut self, core: Cpu) {
|
||||
let dport_control = crate::peripherals::DPORT::PTR;
|
||||
let dport_control = unsafe { &*dport_control };
|
||||
|
||||
match core {
|
||||
Cpu::ProCpu => {
|
||||
dport_control
|
||||
.pro_cache_ctrl
|
||||
.modify(|_, w| w.pro_cache_flush_ena().clear_bit());
|
||||
dport_control
|
||||
.pro_cache_ctrl
|
||||
.modify(|_, w| w.pro_cache_flush_ena().set_bit());
|
||||
while dport_control
|
||||
.pro_cache_ctrl
|
||||
.read()
|
||||
.pro_cache_flush_done()
|
||||
.bit_is_clear()
|
||||
{}
|
||||
|
||||
dport_control
|
||||
.pro_cache_ctrl
|
||||
.modify(|_, w| w.pro_cache_flush_ena().clear_bit());
|
||||
}
|
||||
Cpu::AppCpu => {
|
||||
dport_control
|
||||
.app_cache_ctrl
|
||||
.modify(|_, w| w.app_cache_flush_ena().clear_bit());
|
||||
dport_control
|
||||
.app_cache_ctrl
|
||||
.modify(|_, w| w.app_cache_flush_ena().set_bit());
|
||||
while dport_control
|
||||
.app_cache_ctrl
|
||||
.read()
|
||||
.app_cache_flush_done()
|
||||
.bit_is_clear()
|
||||
{}
|
||||
dport_control
|
||||
.app_cache_ctrl
|
||||
.modify(|_, w| w.app_cache_flush_ena().clear_bit());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn enable_cache(&mut self, core: Cpu) {
|
||||
let spi0 = unsafe { &(*crate::peripherals::SPI0::ptr()) };
|
||||
|
||||
let dport_control = crate::peripherals::DPORT::PTR;
|
||||
let dport_control = unsafe { &*dport_control };
|
||||
|
||||
match core {
|
||||
Cpu::ProCpu => {
|
||||
spi0.cache_fctrl.modify(|_, w| w.cache_req_en().set_bit());
|
||||
dport_control
|
||||
.pro_cache_ctrl
|
||||
.modify(|_, w| w.pro_cache_enable().set_bit());
|
||||
}
|
||||
Cpu::AppCpu => {
|
||||
spi0.cache_fctrl.modify(|_, w| w.cache_req_en().set_bit());
|
||||
dport_control
|
||||
.app_cache_ctrl
|
||||
.modify(|_, w| w.app_cache_enable().set_bit());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn start_core1_init() -> ! {
|
||||
extern "C" {
|
||||
static mut _stack_end_cpu1: u32;
|
||||
}
|
||||
|
||||
// disables interrupts
|
||||
xtensa_lx::interrupt::set_mask(0);
|
||||
|
||||
// reset cycle compare registers
|
||||
xtensa_lx::timer::set_ccompare0(0);
|
||||
xtensa_lx::timer::set_ccompare1(0);
|
||||
xtensa_lx::timer::set_ccompare2(0);
|
||||
|
||||
// set stack pointer to end of memory: no need to retain stack up to this point
|
||||
set_stack_pointer(&mut _stack_end_cpu1);
|
||||
|
||||
extern "C" {
|
||||
static mut _init_start: u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// move vec table
|
||||
let base = &_init_start as *const u32;
|
||||
core::arch::asm!("wsr.vecbase {0}", in(reg) base, options(nostack));
|
||||
}
|
||||
|
||||
match START_CORE1_FUNCTION.take() {
|
||||
Some(entry) => (*entry)(),
|
||||
None => panic!("No start function set"),
|
||||
}
|
||||
|
||||
panic!("Return from second core's entry");
|
||||
}
|
||||
|
||||
/// Start the APP (second) core
|
||||
///
|
||||
/// The second core will start running the closure `entry`.
|
||||
///
|
||||
/// Dropping the returned guard will park the core.
|
||||
pub fn start_app_core<'a>(
|
||||
&mut self,
|
||||
entry: &'a mut (dyn FnMut() + Send),
|
||||
) -> Result<AppCoreGuard<'a>, Error> {
|
||||
let dport_control = crate::peripherals::DPORT::PTR;
|
||||
let dport_control = unsafe { &*dport_control };
|
||||
|
||||
if !xtensa_lx::is_debugger_attached()
|
||||
&& dport_control
|
||||
.appcpu_ctrl_b
|
||||
.read()
|
||||
.appcpu_clkgate_en()
|
||||
.bit_is_set()
|
||||
{
|
||||
return Err(Error::CoreAlreadyRunning);
|
||||
}
|
||||
|
||||
self.flush_cache(Cpu::AppCpu);
|
||||
self.enable_cache(Cpu::AppCpu);
|
||||
|
||||
unsafe {
|
||||
let entry_fn: &'static mut (dyn FnMut() + 'static) = core::mem::transmute(entry);
|
||||
START_CORE1_FUNCTION = Some(entry_fn);
|
||||
}
|
||||
|
||||
dport_control.appcpu_ctrl_d.write(|w| unsafe {
|
||||
w.appcpu_boot_addr()
|
||||
.bits(Self::start_core1_init as *const u32 as u32)
|
||||
});
|
||||
|
||||
dport_control
|
||||
.appcpu_ctrl_b
|
||||
.modify(|_, w| w.appcpu_clkgate_en().set_bit());
|
||||
dport_control
|
||||
.appcpu_ctrl_c
|
||||
.modify(|_, w| w.appcpu_runstall().clear_bit());
|
||||
dport_control
|
||||
.appcpu_ctrl_a
|
||||
.modify(|_, w| w.appcpu_resetting().set_bit());
|
||||
dport_control
|
||||
.appcpu_ctrl_a
|
||||
.modify(|_, w| w.appcpu_resetting().clear_bit());
|
||||
|
||||
self.unpark_core(Cpu::AppCpu);
|
||||
|
||||
Ok(AppCoreGuard {
|
||||
phantom: PhantomData::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
|
||||
# field_name, | efuse_block, | bit_start, | bit_count, |comment #
|
||||
# | (EFUSE_BLK0 | (0..255) | (1-256) | #
|
||||
# | EFUSE_BLK1 | | | #
|
||||
# | ...) | | | #
|
||||
##########################################################################
|
||||
# !!!!!!!!!!! #
|
||||
# After editing this file, run the command manually "idf.py efuse-common-table"
|
||||
# this will generate new source files, next rebuild all the sources.
|
||||
# !!!!!!!!!!! #
|
||||
|
||||
# This file was generated by regtools.py based on the efuses.yaml file with the version: 369d2d860d34e777c0f7d545a7dfc3c4
|
||||
|
||||
WR_DIS, EFUSE_BLK0, 0, 16, [] Efuse write disable mask
|
||||
WR_DIS.RD_DIS, EFUSE_BLK0, 0, 1, [WR_DIS.EFUSE_RD_DISABLE] wr_dis of RD_DIS
|
||||
WR_DIS.WR_DIS, EFUSE_BLK0, 1, 1, [] wr_dis of WR_DIS
|
||||
WR_DIS.FLASH_CRYPT_CNT, EFUSE_BLK0, 2, 1, [] wr_dis of FLASH_CRYPT_CNT
|
||||
WR_DIS.UART_DOWNLOAD_DIS, EFUSE_BLK0, 2, 1, [] wr_dis of UART_DOWNLOAD_DIS
|
||||
WR_DIS.MAC, EFUSE_BLK0, 3, 1, [WR_DIS.MAC_FACTORY] wr_dis of MAC
|
||||
WR_DIS.MAC_CRC, EFUSE_BLK0, 3, 1, [WR_DIS.MAC_FACTORY_CRC] wr_dis of MAC_CRC
|
||||
WR_DIS.DISABLE_APP_CPU, EFUSE_BLK0, 3, 1, [WR_DIS.CHIP_VER_DIS_APP_CPU] wr_dis of DISABLE_APP_CPU
|
||||
WR_DIS.DISABLE_BT, EFUSE_BLK0, 3, 1, [WR_DIS.CHIP_VER_DIS_BT] wr_dis of DISABLE_BT
|
||||
WR_DIS.DIS_CACHE, EFUSE_BLK0, 3, 1, [WR_DIS.CHIP_VER_DIS_CACHE] wr_dis of DIS_CACHE
|
||||
WR_DIS.VOL_LEVEL_HP_INV, EFUSE_BLK0, 3, 1, [] wr_dis of VOL_LEVEL_HP_INV
|
||||
WR_DIS.CLK8M_FREQ, EFUSE_BLK0, 4, 1, [WR_DIS.CK8M_FREQ] wr_dis of CLK8M_FREQ
|
||||
WR_DIS.ADC_VREF, EFUSE_BLK0, 4, 1, [] wr_dis of ADC_VREF
|
||||
WR_DIS.XPD_SDIO_REG, EFUSE_BLK0, 5, 1, [] wr_dis of XPD_SDIO_REG
|
||||
WR_DIS.XPD_SDIO_TIEH, EFUSE_BLK0, 5, 1, [WR_DIS.SDIO_TIEH] wr_dis of XPD_SDIO_TIEH
|
||||
WR_DIS.XPD_SDIO_FORCE, EFUSE_BLK0, 5, 1, [WR_DIS.SDIO_FORCE] wr_dis of XPD_SDIO_FORCE
|
||||
WR_DIS.SPI_PAD_CONFIG_CLK, EFUSE_BLK0, 6, 1, [] wr_dis of SPI_PAD_CONFIG_CLK
|
||||
WR_DIS.SPI_PAD_CONFIG_Q, EFUSE_BLK0, 6, 1, [] wr_dis of SPI_PAD_CONFIG_Q
|
||||
WR_DIS.SPI_PAD_CONFIG_D, EFUSE_BLK0, 6, 1, [] wr_dis of SPI_PAD_CONFIG_D
|
||||
WR_DIS.SPI_PAD_CONFIG_CS0, EFUSE_BLK0, 6, 1, [] wr_dis of SPI_PAD_CONFIG_CS0
|
||||
WR_DIS.BLOCK1, EFUSE_BLK0, 7, 1, [WR_DIS.ENCRYPT_FLASH_KEY WR_DIS.BLK1] wr_dis of BLOCK1
|
||||
WR_DIS.BLOCK2, EFUSE_BLK0, 8, 1, [WR_DIS.SECURE_BOOT_KEY WR_DIS.BLK2] wr_dis of BLOCK2
|
||||
WR_DIS.BLOCK3, EFUSE_BLK0, 9, 1, [WR_DIS.BLK3] wr_dis of BLOCK3
|
||||
WR_DIS.CUSTOM_MAC_CRC, EFUSE_BLK0, 9, 1, [WR_DIS.MAC_CUSTOM_CRC] wr_dis of CUSTOM_MAC_CRC
|
||||
WR_DIS.CUSTOM_MAC, EFUSE_BLK0, 9, 1, [WR_DIS.MAC_CUSTOM] wr_dis of CUSTOM_MAC
|
||||
WR_DIS.ADC1_TP_LOW, EFUSE_BLK0, 9, 1, [] wr_dis of ADC1_TP_LOW
|
||||
WR_DIS.ADC1_TP_HIGH, EFUSE_BLK0, 9, 1, [] wr_dis of ADC1_TP_HIGH
|
||||
WR_DIS.ADC2_TP_LOW, EFUSE_BLK0, 9, 1, [] wr_dis of ADC2_TP_LOW
|
||||
WR_DIS.ADC2_TP_HIGH, EFUSE_BLK0, 9, 1, [] wr_dis of ADC2_TP_HIGH
|
||||
WR_DIS.SECURE_VERSION, EFUSE_BLK0, 9, 1, [] wr_dis of SECURE_VERSION
|
||||
WR_DIS.MAC_VERSION, EFUSE_BLK0, 9, 1, [WR_DIS.MAC_CUSTOM_VER] wr_dis of MAC_VERSION
|
||||
WR_DIS.BLK3_PART_RESERVE, EFUSE_BLK0, 10, 1, [] wr_dis of BLK3_PART_RESERVE
|
||||
WR_DIS.FLASH_CRYPT_CONFIG, EFUSE_BLK0, 10, 1, [WR_DIS.ENCRYPT_CONFIG] wr_dis of FLASH_CRYPT_CONFIG
|
||||
WR_DIS.CODING_SCHEME, EFUSE_BLK0, 10, 1, [] wr_dis of CODING_SCHEME
|
||||
WR_DIS.KEY_STATUS, EFUSE_BLK0, 10, 1, [] wr_dis of KEY_STATUS
|
||||
WR_DIS.ABS_DONE_0, EFUSE_BLK0, 12, 1, [] wr_dis of ABS_DONE_0
|
||||
WR_DIS.ABS_DONE_1, EFUSE_BLK0, 13, 1, [] wr_dis of ABS_DONE_1
|
||||
WR_DIS.JTAG_DISABLE, EFUSE_BLK0, 14, 1, [WR_DIS.DISABLE_JTAG] wr_dis of JTAG_DISABLE
|
||||
WR_DIS.CONSOLE_DEBUG_DISABLE, EFUSE_BLK0, 15, 1, [] wr_dis of CONSOLE_DEBUG_DISABLE
|
||||
WR_DIS.DISABLE_DL_ENCRYPT, EFUSE_BLK0, 15, 1, [] wr_dis of DISABLE_DL_ENCRYPT
|
||||
WR_DIS.DISABLE_DL_DECRYPT, EFUSE_BLK0, 15, 1, [] wr_dis of DISABLE_DL_DECRYPT
|
||||
WR_DIS.DISABLE_DL_CACHE, EFUSE_BLK0, 15, 1, [] wr_dis of DISABLE_DL_CACHE
|
||||
RD_DIS, EFUSE_BLK0, 16, 4, [] Disable reading from BlOCK1-3
|
||||
RD_DIS.BLOCK1, EFUSE_BLK0, 16, 1, [RD_DIS.ENCRYPT_FLASH_KEY RD_DIS.BLK1] rd_dis of BLOCK1
|
||||
RD_DIS.BLOCK2, EFUSE_BLK0, 17, 1, [RD_DIS.SECURE_BOOT_KEY RD_DIS.BLK2] rd_dis of BLOCK2
|
||||
RD_DIS.BLOCK3, EFUSE_BLK0, 18, 1, [RD_DIS.BLK3] rd_dis of BLOCK3
|
||||
RD_DIS.CUSTOM_MAC_CRC, EFUSE_BLK0, 18, 1, [RD_DIS.MAC_CUSTOM_CRC] rd_dis of CUSTOM_MAC_CRC
|
||||
RD_DIS.CUSTOM_MAC, EFUSE_BLK0, 18, 1, [RD_DIS.MAC_CUSTOM] rd_dis of CUSTOM_MAC
|
||||
RD_DIS.ADC1_TP_LOW, EFUSE_BLK0, 18, 1, [] rd_dis of ADC1_TP_LOW
|
||||
RD_DIS.ADC1_TP_HIGH, EFUSE_BLK0, 18, 1, [] rd_dis of ADC1_TP_HIGH
|
||||
RD_DIS.ADC2_TP_LOW, EFUSE_BLK0, 18, 1, [] rd_dis of ADC2_TP_LOW
|
||||
RD_DIS.ADC2_TP_HIGH, EFUSE_BLK0, 18, 1, [] rd_dis of ADC2_TP_HIGH
|
||||
RD_DIS.SECURE_VERSION, EFUSE_BLK0, 18, 1, [] rd_dis of SECURE_VERSION
|
||||
RD_DIS.MAC_VERSION, EFUSE_BLK0, 18, 1, [RD_DIS.MAC_CUSTOM_VER] rd_dis of MAC_VERSION
|
||||
RD_DIS.BLK3_PART_RESERVE, EFUSE_BLK0, 19, 1, [] rd_dis of BLK3_PART_RESERVE
|
||||
RD_DIS.FLASH_CRYPT_CONFIG, EFUSE_BLK0, 19, 1, [RD_DIS.ENCRYPT_CONFIG] rd_dis of FLASH_CRYPT_CONFIG
|
||||
RD_DIS.CODING_SCHEME, EFUSE_BLK0, 19, 1, [] rd_dis of CODING_SCHEME
|
||||
RD_DIS.KEY_STATUS, EFUSE_BLK0, 19, 1, [] rd_dis of KEY_STATUS
|
||||
FLASH_CRYPT_CNT, EFUSE_BLK0, 20, 7, [] Flash encryption is enabled if this field has an odd number of bits set
|
||||
UART_DOWNLOAD_DIS, EFUSE_BLK0, 27, 1, [] Disable UART download mode. Valid for ESP32 V3 and newer; only
|
||||
MAC_FACTORY, EFUSE_BLK0, 32, 48, [MAC_FACTORY] MAC address
|
||||
MAC_CRC, EFUSE_BLK0, 80, 8, [MAC_FACTORY_CRC] CRC8 for MAC address
|
||||
DISABLE_APP_CPU, EFUSE_BLK0, 96, 1, [CHIP_VER_DIS_APP_CPU] Disables APP CPU
|
||||
DISABLE_BT, EFUSE_BLK0, 97, 1, [CHIP_VER_DIS_BT] Disables Bluetooth
|
||||
CHIP_PACKAGE_4BIT, EFUSE_BLK0, 98, 1, [CHIP_VER_PKG_4BIT] Chip package identifier #4bit
|
||||
DIS_CACHE, EFUSE_BLK0, 99, 1, [CHIP_VER_DIS_CACHE] Disables cache
|
||||
SPI_PAD_CONFIG_HD, EFUSE_BLK0, 100, 5, [] read for SPI_pad_config_hd
|
||||
CHIP_PACKAGE, EFUSE_BLK0, 105, 3, [CHIP_VER_PKG] Chip package identifier
|
||||
CHIP_CPU_FREQ_LOW, EFUSE_BLK0, 108, 1, [] If set alongside EFUSE_RD_CHIP_CPU_FREQ_RATED; the ESP32's max CPU frequency is rated for 160MHz. 240MHz otherwise
|
||||
CHIP_CPU_FREQ_RATED, EFUSE_BLK0, 109, 1, [] If set; the ESP32's maximum CPU frequency has been rated
|
||||
BLK3_PART_RESERVE, EFUSE_BLK0, 110, 1, [] BLOCK3 partially served for ADC calibration data
|
||||
CHIP_VER_REV1, EFUSE_BLK0, 111, 1, [] bit is set to 1 for rev1 silicon
|
||||
CLK8M_FREQ, EFUSE_BLK0, 128, 8, [CK8M_FREQ] 8MHz clock freq override
|
||||
ADC_VREF, EFUSE_BLK0, 136, 5, [] True ADC reference voltage
|
||||
XPD_SDIO_REG, EFUSE_BLK0, 142, 1, [] read for XPD_SDIO_REG
|
||||
XPD_SDIO_TIEH, EFUSE_BLK0, 143, 1, [SDIO_TIEH] If XPD_SDIO_FORCE & XPD_SDIO_REG {1: "3.3V"; 0: "1.8V"}
|
||||
XPD_SDIO_FORCE, EFUSE_BLK0, 144, 1, [SDIO_FORCE] Ignore MTDI pin (GPIO12) for VDD_SDIO on reset
|
||||
SPI_PAD_CONFIG_CLK, EFUSE_BLK0, 160, 5, [] Override SD_CLK pad (GPIO6/SPICLK)
|
||||
SPI_PAD_CONFIG_Q, EFUSE_BLK0, 165, 5, [] Override SD_DATA_0 pad (GPIO7/SPIQ)
|
||||
SPI_PAD_CONFIG_D, EFUSE_BLK0, 170, 5, [] Override SD_DATA_1 pad (GPIO8/SPID)
|
||||
SPI_PAD_CONFIG_CS0, EFUSE_BLK0, 175, 5, [] Override SD_CMD pad (GPIO11/SPICS0)
|
||||
CHIP_VER_REV2, EFUSE_BLK0, 180, 1, []
|
||||
VOL_LEVEL_HP_INV, EFUSE_BLK0, 182, 2, [] This field stores the voltage level for CPU to run at 240 MHz; or for flash/PSRAM to run at 80 MHz.0x0: level 7; 0x1: level 6; 0x2: level 5; 0x3: level 4. (RO)
|
||||
WAFER_VERSION_MINOR, EFUSE_BLK0, 184, 2, []
|
||||
FLASH_CRYPT_CONFIG, EFUSE_BLK0, 188, 4, [ENCRYPT_CONFIG] Flash encryption config (key tweak bits)
|
||||
CODING_SCHEME, EFUSE_BLK0, 192, 2, [] Efuse variable block length scheme {0: "NONE (BLK1-3 len=256 bits)"; 1: "3/4 (BLK1-3 len=192 bits)"; 2: "REPEAT (BLK1-3 len=128 bits) not supported"; 3: "NONE (BLK1-3 len=256 bits)"}
|
||||
CONSOLE_DEBUG_DISABLE, EFUSE_BLK0, 194, 1, [] Disable ROM BASIC interpreter fallback
|
||||
DISABLE_SDIO_HOST, EFUSE_BLK0, 195, 1, []
|
||||
ABS_DONE_0, EFUSE_BLK0, 196, 1, [] Secure boot V1 is enabled for bootloader image
|
||||
ABS_DONE_1, EFUSE_BLK0, 197, 1, [] Secure boot V2 is enabled for bootloader image
|
||||
JTAG_DISABLE, EFUSE_BLK0, 198, 1, [DISABLE_JTAG] Disable JTAG
|
||||
DISABLE_DL_ENCRYPT, EFUSE_BLK0, 199, 1, [] Disable flash encryption in UART bootloader
|
||||
DISABLE_DL_DECRYPT, EFUSE_BLK0, 200, 1, [] Disable flash decryption in UART bootloader
|
||||
DISABLE_DL_CACHE, EFUSE_BLK0, 201, 1, [] Disable flash cache in UART bootloader
|
||||
KEY_STATUS, EFUSE_BLK0, 202, 1, [] Usage of efuse block 3 (reserved)
|
||||
BLOCK1, EFUSE_BLK1, 0, MAX_BLK_LEN, [ENCRYPT_FLASH_KEY] Flash encryption key
|
||||
BLOCK2, EFUSE_BLK2, 0, MAX_BLK_LEN, [SECURE_BOOT_KEY] Security boot key
|
||||
CUSTOM_MAC_CRC, EFUSE_BLK3, 0, 8, [MAC_CUSTOM_CRC] CRC8 for custom MAC address
|
||||
MAC_CUSTOM, EFUSE_BLK3, 8, 48, [MAC_CUSTOM] Custom MAC address
|
||||
ADC1_TP_LOW, EFUSE_BLK3, 96, 7, [] ADC1 Two Point calibration low point. Only valid if EFUSE_RD_BLK3_PART_RESERVE
|
||||
ADC1_TP_HIGH, EFUSE_BLK3, 103, 9, [] ADC1 Two Point calibration high point. Only valid if EFUSE_RD_BLK3_PART_RESERVE
|
||||
ADC2_TP_LOW, EFUSE_BLK3, 112, 7, [] ADC2 Two Point calibration low point. Only valid if EFUSE_RD_BLK3_PART_RESERVE
|
||||
ADC2_TP_HIGH, EFUSE_BLK3, 119, 9, [] ADC2 Two Point calibration high point. Only valid if EFUSE_RD_BLK3_PART_RESERVE
|
||||
SECURE_VERSION, EFUSE_BLK3, 128, 32, [] Secure version for anti-rollback
|
||||
MAC_VERSION, EFUSE_BLK3, 184, 8, [MAC_CUSTOM_VER] Version of the MAC field {1: "Custom MAC in BLOCK3"}
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 53.
|
@ -1,116 +0,0 @@
|
||||
//! Reading of eFuses
|
||||
|
||||
use fugit::{HertzU32, RateExtU32};
|
||||
|
||||
use crate::peripherals::EFUSE;
|
||||
pub use crate::soc::efuse_field::*;
|
||||
|
||||
pub struct Efuse;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum ChipType {
|
||||
Esp32D0wdq6,
|
||||
Esp32D0wdq5,
|
||||
Esp32D2wdq5,
|
||||
Esp32Picod2,
|
||||
Esp32Picod4,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Efuse {
|
||||
/// Reads chip's MAC address from the eFuse storage.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let mac_address = Efuse::get_mac_address();
|
||||
/// writeln!(
|
||||
/// serial_tx,
|
||||
/// "MAC: {:#X}:{:#X}:{:#X}:{:#X}:{:#X}:{:#X}",
|
||||
/// mac_address[0],
|
||||
/// mac_address[1],
|
||||
/// mac_address[2],
|
||||
/// mac_address[3],
|
||||
/// mac_address[4],
|
||||
/// mac_address[5]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn get_mac_address() -> [u8; 6] {
|
||||
Self::read_field_be(MAC_FACTORY)
|
||||
}
|
||||
|
||||
/// Returns the number of CPUs available on the chip.
|
||||
///
|
||||
/// While ESP32 chips usually come with two mostly equivalent CPUs (protocol
|
||||
/// CPU and application CPU), the application CPU is unavailable on
|
||||
/// some.
|
||||
pub fn get_core_count() -> u32 {
|
||||
if Self::read_field_le::<bool>(DISABLE_APP_CPU) {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum rated clock of the CPU in MHz.
|
||||
///
|
||||
/// Note that the actual clock may be lower, depending on the current power
|
||||
/// configuration of the chip, clock source, and other settings.
|
||||
pub fn get_max_cpu_frequency() -> HertzU32 {
|
||||
let has_rating = Self::read_field_le::<bool>(CHIP_CPU_FREQ_RATED);
|
||||
let has_low_rating = Self::read_field_le::<bool>(CHIP_CPU_FREQ_LOW);
|
||||
|
||||
if has_rating && has_low_rating {
|
||||
160u32.MHz()
|
||||
} else {
|
||||
240u32.MHz()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CHIP_VER_DIS_BT eFuse value.
|
||||
pub fn is_bluetooth_enabled() -> bool {
|
||||
!Self::read_field_le::<bool>(DISABLE_BT)
|
||||
}
|
||||
|
||||
/// Returns the CHIP_VER_PKG eFuse value.
|
||||
pub fn get_chip_type() -> ChipType {
|
||||
let chip_ver = Self::read_field_le::<u8>(CHIP_PACKAGE)
|
||||
| Self::read_field_le::<u8>(CHIP_PACKAGE_4BIT) << 4;
|
||||
|
||||
match chip_ver {
|
||||
0 => ChipType::Esp32D0wdq6,
|
||||
1 => ChipType::Esp32D0wdq5,
|
||||
2 => ChipType::Esp32D2wdq5,
|
||||
4 => ChipType::Esp32Picod2,
|
||||
5 => ChipType::Esp32Picod4,
|
||||
_ => ChipType::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of SPI boot encryption.
|
||||
pub fn get_flash_encryption() -> bool {
|
||||
(Self::read_field_le::<u8>(FLASH_CRYPT_CNT).count_ones() % 2) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum EfuseBlock {
|
||||
Block0,
|
||||
Block1,
|
||||
Block2,
|
||||
Block3,
|
||||
}
|
||||
|
||||
impl EfuseBlock {
|
||||
pub(crate) fn address(self) -> *const u32 {
|
||||
use EfuseBlock::*;
|
||||
let efuse = unsafe { &*EFUSE::ptr() };
|
||||
match self {
|
||||
Block0 => efuse.blk0_rdata0.as_ptr(),
|
||||
Block1 => efuse.blk1_rdata0.as_ptr(),
|
||||
Block2 => efuse.blk2_rdata0.as_ptr(),
|
||||
Block3 => efuse.blk3_rdata0.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,754 +0,0 @@
|
||||
use paste::paste;
|
||||
|
||||
use crate::{
|
||||
gpio::{
|
||||
AlternateFunction,
|
||||
GpioPin,
|
||||
InterruptStatusRegisterAccess,
|
||||
InterruptStatusRegisterAccessBank0,
|
||||
InterruptStatusRegisterAccessBank1,
|
||||
Unknown,
|
||||
},
|
||||
peripherals::GPIO,
|
||||
Cpu,
|
||||
};
|
||||
|
||||
pub const NUM_PINS: usize = 39;
|
||||
|
||||
pub type OutputSignalType = u16;
|
||||
pub const OUTPUT_SIGNAL_MAX: u16 = 548;
|
||||
pub const INPUT_SIGNAL_MAX: u16 = 539;
|
||||
|
||||
pub const ONE_INPUT: u8 = 0x38;
|
||||
pub const ZERO_INPUT: u8 = 0x30;
|
||||
|
||||
pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function2;
|
||||
|
||||
pub(crate) fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO0 {
|
||||
unsafe {
|
||||
let iomux = &*crate::peripherals::IO_MUX::PTR;
|
||||
|
||||
match gpio_num {
|
||||
0 => core::mem::transmute(&(iomux.gpio0)),
|
||||
1 => core::mem::transmute(&(iomux.gpio1)),
|
||||
2 => core::mem::transmute(&(iomux.gpio2)),
|
||||
3 => core::mem::transmute(&(iomux.gpio3)),
|
||||
4 => core::mem::transmute(&(iomux.gpio4)),
|
||||
5 => core::mem::transmute(&(iomux.gpio5)),
|
||||
6 => core::mem::transmute(&(iomux.gpio6)),
|
||||
7 => core::mem::transmute(&(iomux.gpio7)),
|
||||
8 => core::mem::transmute(&(iomux.gpio8)),
|
||||
9 => core::mem::transmute(&(iomux.gpio9)),
|
||||
10 => core::mem::transmute(&(iomux.gpio10)),
|
||||
11 => core::mem::transmute(&(iomux.gpio11)),
|
||||
12 => core::mem::transmute(&(iomux.gpio12)),
|
||||
13 => core::mem::transmute(&(iomux.gpio13)),
|
||||
14 => core::mem::transmute(&(iomux.gpio14)),
|
||||
15 => core::mem::transmute(&(iomux.gpio15)),
|
||||
16 => core::mem::transmute(&(iomux.gpio16)),
|
||||
17 => core::mem::transmute(&(iomux.gpio17)),
|
||||
18 => core::mem::transmute(&(iomux.gpio18)),
|
||||
19 => core::mem::transmute(&(iomux.gpio19)),
|
||||
20 => core::mem::transmute(&(iomux.gpio20)),
|
||||
21 => core::mem::transmute(&(iomux.gpio21)),
|
||||
22 => core::mem::transmute(&(iomux.gpio22)),
|
||||
23 => core::mem::transmute(&(iomux.gpio23)),
|
||||
24 => core::mem::transmute(&(iomux.gpio24)),
|
||||
25 => core::mem::transmute(&(iomux.gpio25)),
|
||||
26 => core::mem::transmute(&(iomux.gpio26)),
|
||||
27 => core::mem::transmute(&(iomux.gpio27)),
|
||||
32 => core::mem::transmute(&(iomux.gpio32)),
|
||||
33 => core::mem::transmute(&(iomux.gpio33)),
|
||||
34 => core::mem::transmute(&(iomux.gpio34)),
|
||||
35 => core::mem::transmute(&(iomux.gpio35)),
|
||||
36 => core::mem::transmute(&(iomux.gpio36)),
|
||||
37 => core::mem::transmute(&(iomux.gpio37)),
|
||||
38 => core::mem::transmute(&(iomux.gpio38)),
|
||||
39 => core::mem::transmute(&(iomux.gpio39)),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 {
|
||||
match crate::get_core() {
|
||||
Cpu::AppCpu => int_enable as u8 | ((nmi_enable as u8) << 1),
|
||||
// this should be bits 3 & 4 respectively, according to the TRM, but it doesn't seem to
|
||||
// work. This does though.
|
||||
Cpu::ProCpu => (int_enable as u8) << 2 | ((nmi_enable as u8) << 3),
|
||||
}
|
||||
}
|
||||
|
||||
/// Peripheral input signals for the GPIO mux
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum InputSignal {
|
||||
SPICLK = 0,
|
||||
SPIQ = 1,
|
||||
SPID = 2,
|
||||
SPIHD = 3,
|
||||
SPIWP = 4,
|
||||
SPICS0 = 5,
|
||||
SPICS1 = 6,
|
||||
SPICS2 = 7,
|
||||
HSPICLK = 8,
|
||||
HSPIQ = 9,
|
||||
HSPID = 10,
|
||||
HSPICS0 = 11,
|
||||
HSPIHD = 12,
|
||||
HSPIWP = 13,
|
||||
U0RXD = 14,
|
||||
U0CTS = 15,
|
||||
U0DSR = 16,
|
||||
U1RXD = 17,
|
||||
U1CTS = 18,
|
||||
I2CM_SDA = 20,
|
||||
EXT_I2C_SDA = 22,
|
||||
I2S0O_BCK = 23,
|
||||
I2S1O_BCK = 24,
|
||||
I2S0O_WS = 25,
|
||||
I2S1O_WS = 26,
|
||||
I2S0I_BCK = 27,
|
||||
I2S0I_WS = 28,
|
||||
I2CEXT0_SCL = 29,
|
||||
I2CEXT0_SDA = 30,
|
||||
PWM0_SYNC0 = 31,
|
||||
PWM0_SYNC1 = 32,
|
||||
PWM0_SYNC2 = 33,
|
||||
PWM0_F0 = 34,
|
||||
PWM0_F1 = 35,
|
||||
PWM0_F2 = 36,
|
||||
GPIO_BT_ACTIVE = 37,
|
||||
GPIO_BT_PRIORITY = 38,
|
||||
PCNT0_SIG_CH0 = 39,
|
||||
PCNT0_SIG_CH1 = 40,
|
||||
PCNT0_CTRL_CH0 = 41,
|
||||
PCNT0_CTRL_CH1 = 42,
|
||||
PCNT1_SIG_CH0 = 43,
|
||||
PCNT1_SIG_CH1 = 44,
|
||||
PCNT1_CTRL_CH0 = 45,
|
||||
PCNT1_CTRL_CH1 = 46,
|
||||
PCNT2_SIG_CH0 = 47,
|
||||
PCNT2_SIG_CH1 = 48,
|
||||
PCNT2_CTRL_CH0 = 49,
|
||||
PCNT2_CTRL_CH1 = 50,
|
||||
PCNT3_SIG_CH0 = 51,
|
||||
PCNT3_SIG_CH1 = 52,
|
||||
PCNT3_CTRL_CH0 = 53,
|
||||
PCNT3_CTRL_CH1 = 54,
|
||||
PCNT4_SIG_CH0 = 55,
|
||||
PCNT4_SIG_CH1 = 56,
|
||||
PCNT4_CTRL_CH0 = 57,
|
||||
PCNT4_CTRL_CH1 = 58,
|
||||
HSPICS1 = 61,
|
||||
HSPICS2 = 62,
|
||||
VSPICLK = 63,
|
||||
VSPIQ = 64,
|
||||
VSPID = 65,
|
||||
VSPIHD = 66,
|
||||
VSPIWP = 67,
|
||||
VSPICS0 = 68,
|
||||
VSPICS1 = 69,
|
||||
VSPICS2 = 70,
|
||||
PCNT5_SIG_CH0 = 71,
|
||||
PCNT5_SIG_CH1 = 72,
|
||||
PCNT5_CTRL_CH0 = 73,
|
||||
PCNT5_CTRL_CH1 = 74,
|
||||
PCNT6_SIG_CH0 = 75,
|
||||
PCNT6_SIG_CH1 = 76,
|
||||
PCNT6_CTRL_CH0 = 77,
|
||||
PCNT6_CTRL_CH1 = 78,
|
||||
PCNT7_SIG_CH0 = 79,
|
||||
PCNT7_SIG_CH1 = 80,
|
||||
PCNT7_CTRL_CH0 = 81,
|
||||
PCNT7_CTRL_CH1 = 82,
|
||||
RMT_SIG_0 = 83,
|
||||
RMT_SIG_1 = 84,
|
||||
RMT_SIG_2 = 85,
|
||||
RMT_SIG_3 = 86,
|
||||
RMT_SIG_4 = 87,
|
||||
RMT_SIG_5 = 88,
|
||||
RMT_SIG_6 = 89,
|
||||
RMT_SIG_7 = 90,
|
||||
EXT_ADC_START = 93,
|
||||
CAN_RX = 94,
|
||||
I2CEXT1_SCL = 95,
|
||||
I2CEXT1_SDA = 96,
|
||||
HOST_CARD_DETECT_N_1 = 97,
|
||||
HOST_CARD_DETECT_N_2 = 98,
|
||||
HOST_CARD_WRITE_PRT_1 = 99,
|
||||
HOST_CARD_WRITE_PRT_2 = 100,
|
||||
HOST_CARD_INT_N_1 = 101,
|
||||
HOST_CARD_INT_N_2 = 102,
|
||||
PWM1_SYNC0 = 103,
|
||||
PWM1_SYNC1 = 104,
|
||||
PWM1_SYNC2 = 105,
|
||||
PWM1_F0 = 106,
|
||||
PWM1_F1 = 107,
|
||||
PWM1_F2 = 108,
|
||||
PWM0_CAP0 = 109,
|
||||
PWM0_CAP1 = 110,
|
||||
PWM0_CAP2 = 111,
|
||||
PWM1_CAP0 = 112,
|
||||
PWM1_CAP1 = 113,
|
||||
PWM1_CAP2 = 114,
|
||||
PWM2_FLTA = 115,
|
||||
PWM2_FLTB = 116,
|
||||
PWM2_CAP1 = 117,
|
||||
PWM2_CAP2 = 118,
|
||||
PWM2_CAP3 = 119,
|
||||
PWM3_FLTA = 120,
|
||||
PWM3_FLTB = 121,
|
||||
PWM3_CAP1 = 122,
|
||||
PWM3_CAP2 = 123,
|
||||
PWM3_CAP3 = 124,
|
||||
CAN_CLKOUT = 125,
|
||||
SPID4 = 128,
|
||||
SPID5 = 129,
|
||||
SPID6 = 130,
|
||||
SPID7 = 131,
|
||||
HSPID4 = 132,
|
||||
HSPID5 = 133,
|
||||
HSPID6 = 134,
|
||||
HSPID7 = 135,
|
||||
VSPID4 = 136,
|
||||
VSPID5 = 137,
|
||||
VSPID6 = 138,
|
||||
VSPID7 = 139,
|
||||
I2S0I_DATA_0 = 140,
|
||||
I2S0I_DATA_1 = 141,
|
||||
I2S0I_DATA_2 = 142,
|
||||
I2S0I_DATA_3 = 143,
|
||||
I2S0I_DATA_4 = 144,
|
||||
I2S0I_DATA_5 = 145,
|
||||
I2S0I_DATA_6 = 146,
|
||||
I2S0I_DATA_7 = 147,
|
||||
I2S0I_DATA_8 = 148,
|
||||
I2S0I_DATA_9 = 149,
|
||||
I2S0I_DATA_10 = 150,
|
||||
I2S0I_DATA_11 = 151,
|
||||
I2S0I_DATA_12 = 152,
|
||||
I2S0I_DATA_13 = 153,
|
||||
I2S0I_DATA_14 = 154,
|
||||
I2S0I_DATA_15 = 155,
|
||||
I2S1I_BCK = 164,
|
||||
I2S1I_WS = 165,
|
||||
I2S1I_DATA_0 = 166,
|
||||
I2S1I_DATA_1 = 167,
|
||||
I2S1I_DATA_2 = 168,
|
||||
I2S1I_DATA_3 = 169,
|
||||
I2S1I_DATA_4 = 170,
|
||||
I2S1I_DATA_5 = 171,
|
||||
I2S1I_DATA_6 = 172,
|
||||
I2S1I_DATA_7 = 173,
|
||||
I2S1I_DATA_8 = 174,
|
||||
I2S1I_DATA_9 = 175,
|
||||
I2S1I_DATA_10 = 176,
|
||||
I2S1I_DATA_11 = 177,
|
||||
I2S1I_DATA_12 = 178,
|
||||
I2S1I_DATA_13 = 179,
|
||||
I2S1I_DATA_14 = 180,
|
||||
I2S1I_DATA_15 = 181,
|
||||
I2S0I_H_SYNC = 190,
|
||||
I2S0I_V_SYNC = 191,
|
||||
I2S0I_H_ENABLE = 192,
|
||||
I2S1I_H_SYNC = 193,
|
||||
I2S1I_V_SYNC = 194,
|
||||
I2S1I_H_ENABLE = 195,
|
||||
U2RXD = 198,
|
||||
U2CTS = 199,
|
||||
EMAC_MDC = 200,
|
||||
EMAC_MDI = 201,
|
||||
EMAC_CRS = 202,
|
||||
EMAC_COL = 203,
|
||||
PCMFSYNC = 204,
|
||||
PCMCLK = 205,
|
||||
PCMDIN = 206,
|
||||
SIG_IN_FUNC224 = 224,
|
||||
SIG_IN_FUNC225 = 225,
|
||||
SIG_IN_FUNC226 = 226,
|
||||
SIG_IN_FUNC227 = 227,
|
||||
SIG_IN_FUNC228 = 228,
|
||||
|
||||
SD_DATA0 = 512,
|
||||
SD_DATA1,
|
||||
SD_DATA2,
|
||||
SD_DATA3,
|
||||
HS1_DATA0,
|
||||
HS1_DATA1,
|
||||
HS1_DATA2,
|
||||
HS1_DATA3,
|
||||
HS1_DATA4,
|
||||
HS1_DATA5,
|
||||
HS1_DATA6,
|
||||
HS1_DATA7,
|
||||
HS2_DATA0,
|
||||
HS2_DATA1,
|
||||
HS2_DATA2,
|
||||
HS2_DATA3,
|
||||
|
||||
EMAC_TX_CLK,
|
||||
EMAC_RXD2,
|
||||
EMAC_TX_ER,
|
||||
EMAC_RX_CLK,
|
||||
EMAC_RX_ER,
|
||||
EMAC_RXD3,
|
||||
EMAC_RXD0,
|
||||
EMAC_RXD1,
|
||||
EMAC_RX_DV,
|
||||
|
||||
MTDI,
|
||||
MTCK,
|
||||
MTMS,
|
||||
}
|
||||
|
||||
/// Peripheral output signals for the GPIO mux
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum OutputSignal {
|
||||
SPICLK = 0,
|
||||
SPIQ = 1,
|
||||
SPID = 2,
|
||||
SPIHD = 3,
|
||||
SPIWP = 4,
|
||||
SPICS0 = 5,
|
||||
SPICS1 = 6,
|
||||
SPICS2 = 7,
|
||||
HSPICLK = 8,
|
||||
HSPIQ = 9,
|
||||
HSPID = 10,
|
||||
HSPICS0 = 11,
|
||||
HSPIHD = 12,
|
||||
HSPIWP = 13,
|
||||
U0TXD = 14,
|
||||
U0RTS = 15,
|
||||
U0DTR = 16,
|
||||
U1TXD = 17,
|
||||
U1RTS = 18,
|
||||
I2CM_SCL = 19,
|
||||
I2CM_SDA = 20,
|
||||
EXT2C_SCL = 21,
|
||||
EXT2C_SDA = 22,
|
||||
I2S0O_BCK = 23,
|
||||
I2S1O_BCK = 24,
|
||||
I2S0O_WS = 25,
|
||||
I2S1O_WS = 26,
|
||||
I2S0I_BCK = 27,
|
||||
I2S0I_WS = 28,
|
||||
I2CEXT0_SCL = 29,
|
||||
I2CEXT0_SDA = 30,
|
||||
SDIO_TOHOSTT = 31,
|
||||
PWM0_0A = 32,
|
||||
PWM0_0B = 33,
|
||||
PWM0_1A = 34,
|
||||
PWM0_1B = 35,
|
||||
PWM0_2A = 36,
|
||||
PWM0_2B = 37,
|
||||
GPIO_WLAN_ACTIVE = 40,
|
||||
BB_DIAG0 = 41,
|
||||
BB_DIAG1 = 42,
|
||||
BB_DIAG2 = 43,
|
||||
BB_DIAG3 = 44,
|
||||
BB_DIAG4 = 45,
|
||||
BB_DIAG5 = 46,
|
||||
BB_DIAG6 = 47,
|
||||
BB_DIAG7 = 48,
|
||||
BB_DIAG8 = 49,
|
||||
BB_DIAG9 = 50,
|
||||
BB_DIAG10 = 51,
|
||||
BB_DIAG11 = 52,
|
||||
BB_DIAG12 = 53,
|
||||
BB_DIAG13 = 54,
|
||||
BB_DIAG14 = 55,
|
||||
BB_DIAG15 = 56,
|
||||
BB_DIAG16 = 57,
|
||||
BB_DIAG17 = 58,
|
||||
BB_DIAG18 = 59,
|
||||
BB_DIAG19 = 60,
|
||||
HSPICS1 = 61,
|
||||
HSPICS2 = 62,
|
||||
VSPICLK = 63,
|
||||
VSPIQ = 64,
|
||||
VSPID = 65,
|
||||
VSPIHD = 66,
|
||||
VSPIWP = 67,
|
||||
VSPICS0 = 68,
|
||||
VSPICS1 = 69,
|
||||
VSPICS2 = 70,
|
||||
LEDC_HS_SIG0 = 71,
|
||||
LEDC_HS_SIG1 = 72,
|
||||
LEDC_HS_SIG2 = 73,
|
||||
LEDC_HS_SIG3 = 74,
|
||||
LEDC_HS_SIG4 = 75,
|
||||
LEDC_HS_SIG5 = 76,
|
||||
LEDC_HS_SIG6 = 77,
|
||||
LEDC_HS_SIG7 = 78,
|
||||
LEDC_LS_SIG0 = 79,
|
||||
LEDC_LS_SIG1 = 80,
|
||||
LEDC_LS_SIG2 = 81,
|
||||
LEDC_LS_SIG3 = 82,
|
||||
LEDC_LS_SIG4 = 83,
|
||||
LEDC_LS_SIG5 = 84,
|
||||
LEDC_LS_SIG6 = 85,
|
||||
LEDC_LS_SIG7 = 86,
|
||||
RMT_SIG_0 = 87,
|
||||
RMT_SIG_1 = 88,
|
||||
RMT_SIG_2 = 89,
|
||||
RMT_SIG_3 = 90,
|
||||
RMT_SIG_4 = 91,
|
||||
RMT_SIG_5 = 92,
|
||||
RMT_SIG_6 = 93,
|
||||
RMT_SIG_7 = 94,
|
||||
I2CEXT1_SCL = 95,
|
||||
I2CEXT1_SDA = 96,
|
||||
HOST_CCMD_OD_PULLUP_EN_N = 97,
|
||||
HOST_RST_N_1 = 98,
|
||||
HOST_RST_N_2 = 99,
|
||||
GPIO_SD0 = 100,
|
||||
GPIO_SD1 = 101,
|
||||
GPIO_SD2 = 102,
|
||||
GPIO_SD3 = 103,
|
||||
GPIO_SD4 = 104,
|
||||
GPIO_SD5 = 105,
|
||||
GPIO_SD6 = 106,
|
||||
GPIO_SD7 = 107,
|
||||
PWM1_0A = 108,
|
||||
PWM1_0B = 109,
|
||||
PWM1_1A = 110,
|
||||
PWM1_1B = 111,
|
||||
PWM1_2A = 112,
|
||||
PWM1_2B = 113,
|
||||
PWM2_1H = 114,
|
||||
PWM2_1L = 115,
|
||||
PWM2_2H = 116,
|
||||
PWM2_2L = 117,
|
||||
PWM2_3H = 118,
|
||||
PWM2_3L = 119,
|
||||
PWM2_4H = 120,
|
||||
PWM2_4L = 121,
|
||||
CAN_TX = 123,
|
||||
CAN_BUS_OFF_ON = 124,
|
||||
SPID4 = 128,
|
||||
SPID5 = 129,
|
||||
SPID6 = 130,
|
||||
SPID7 = 131,
|
||||
HSPID4 = 132,
|
||||
HSPID5 = 133,
|
||||
HSPID6 = 134,
|
||||
HSPID7 = 135,
|
||||
VSPID4 = 136,
|
||||
VSPID5 = 137,
|
||||
VSPID6 = 138,
|
||||
VSPID7 = 139,
|
||||
I2S0O_DATA_0 = 140,
|
||||
I2S0O_DATA_1 = 141,
|
||||
I2S0O_DATA_2 = 142,
|
||||
I2S0O_DATA_3 = 143,
|
||||
I2S0O_DATA_4 = 144,
|
||||
I2S0O_DATA_5 = 145,
|
||||
I2S0O_DATA_6 = 146,
|
||||
I2S0O_DATA_7 = 147,
|
||||
I2S0O_DATA_8 = 148,
|
||||
I2S0O_DATA_9 = 149,
|
||||
I2S0O_DATA_10 = 150,
|
||||
I2S0O_DATA_11 = 151,
|
||||
I2S0O_DATA_12 = 152,
|
||||
I2S0O_DATA_13 = 153,
|
||||
I2S0O_DATA_14 = 154,
|
||||
I2S0O_DATA_15 = 155,
|
||||
I2S0O_DATA_16 = 156,
|
||||
I2S0O_DATA_17 = 157,
|
||||
I2S0O_DATA_18 = 158,
|
||||
I2S0O_DATA_19 = 159,
|
||||
I2S0O_DATA_20 = 160,
|
||||
I2S0O_DATA_21 = 161,
|
||||
I2S0O_DATA_22 = 162,
|
||||
I2S0O_DATA_23 = 163,
|
||||
I2S1I_BCK = 164,
|
||||
I2S1I_WS = 165,
|
||||
I2S1O_DATA_0 = 166,
|
||||
I2S1O_DATA_1 = 167,
|
||||
I2S1O_DATA_2 = 168,
|
||||
I2S1O_DATA_3 = 169,
|
||||
I2S1O_DATA_4 = 170,
|
||||
I2S1O_DATA_5 = 171,
|
||||
I2S1O_DATA_6 = 172,
|
||||
I2S1O_DATA_7 = 173,
|
||||
I2S1O_DATA_8 = 174,
|
||||
I2S1O_DATA_9 = 175,
|
||||
I2S1O_DATA_10 = 176,
|
||||
I2S1O_DATA_11 = 177,
|
||||
I2S1O_DATA_12 = 178,
|
||||
I2S1O_DATA_13 = 179,
|
||||
I2S1O_DATA_14 = 180,
|
||||
I2S1O_DATA_15 = 181,
|
||||
I2S1O_DATA_16 = 182,
|
||||
I2S1O_DATA_17 = 183,
|
||||
I2S1O_DATA_18 = 184,
|
||||
I2S1O_DATA_19 = 185,
|
||||
I2S1O_DATA_20 = 186,
|
||||
I2S1O_DATA_21 = 187,
|
||||
I2S1O_DATA_22 = 188,
|
||||
I2S1O_DATA_23 = 189,
|
||||
PWM3_1H = 190,
|
||||
PWM3_1L = 191,
|
||||
PWM3_2H = 192,
|
||||
PWM3_2L = 193,
|
||||
PWM3_3H = 194,
|
||||
PWM3_3L = 195,
|
||||
PWM3_4H = 196,
|
||||
PWM3_4L = 197,
|
||||
U2TXD = 198,
|
||||
U2RTS = 199,
|
||||
EMAC_MDC = 200,
|
||||
EMAC_MDO = 201,
|
||||
EMAC_CRS = 202,
|
||||
EMAC_COL = 203,
|
||||
BT_AUDIO0RQ = 204,
|
||||
BT_AUDIO1RQ = 205,
|
||||
BT_AUDIO2RQ = 206,
|
||||
BLE_AUDIO0RQ = 207,
|
||||
BLE_AUDIO1RQ = 208,
|
||||
BLE_AUDIO2RQ = 209,
|
||||
PCMFSYNC = 210,
|
||||
PCMCLK = 211,
|
||||
PCMDOUT = 212,
|
||||
BLE_AUDIO_SYNC0_P = 213,
|
||||
BLE_AUDIO_SYNC1_P = 214,
|
||||
BLE_AUDIO_SYNC2_P = 215,
|
||||
ANT_SEL0 = 216,
|
||||
ANT_SEL1 = 217,
|
||||
ANT_SEL2 = 218,
|
||||
ANT_SEL3 = 219,
|
||||
ANT_SEL4 = 220,
|
||||
ANT_SEL5 = 221,
|
||||
ANT_SEL6 = 222,
|
||||
ANT_SEL7 = 223,
|
||||
SIGNAL_224 = 224,
|
||||
SIGNAL_225 = 225,
|
||||
SIGNAL_226 = 226,
|
||||
SIGNAL_227 = 227,
|
||||
SIGNAL_228 = 228,
|
||||
GPIO = 256,
|
||||
|
||||
CLK_OUT1 = 512,
|
||||
CLK_OUT2,
|
||||
CLK_OUT3,
|
||||
SD_CLK,
|
||||
SD_CMD,
|
||||
SD_DATA0,
|
||||
SD_DATA1,
|
||||
SD_DATA2,
|
||||
SD_DATA3,
|
||||
HS1_CLK,
|
||||
HS1_CMD,
|
||||
HS1_DATA0,
|
||||
HS1_DATA1,
|
||||
HS1_DATA2,
|
||||
HS1_DATA3,
|
||||
HS1_DATA4,
|
||||
HS1_DATA5,
|
||||
HS1_DATA6,
|
||||
HS1_DATA7,
|
||||
HS1_STROBE,
|
||||
HS2_CLK,
|
||||
HS2_CMD,
|
||||
HS2_DATA0,
|
||||
HS2_DATA1,
|
||||
HS2_DATA2,
|
||||
HS2_DATA3,
|
||||
|
||||
EMAC_TX_CLK,
|
||||
EMAC_TX_ER,
|
||||
EMAC_TXD3,
|
||||
EMAC_RX_ER,
|
||||
EMAC_TXD2,
|
||||
EMAC_CLK_OUT,
|
||||
EMAC_CLK_180,
|
||||
EMAC_TXD0,
|
||||
EMAC_TX_EN,
|
||||
EMAC_TXD1,
|
||||
|
||||
MTDO,
|
||||
}
|
||||
|
||||
pub(crate) fn errata36(pin_num: u8, pull_up: bool, pull_down: bool) {
|
||||
use crate::peripherals::RTC_IO;
|
||||
let rtcio = unsafe { &*RTC_IO::PTR };
|
||||
|
||||
match pin_num {
|
||||
0 => {
|
||||
rtcio
|
||||
.touch_pad1
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
2 => {
|
||||
rtcio
|
||||
.touch_pad2
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
4 => {
|
||||
rtcio
|
||||
.touch_pad0
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
12 => {
|
||||
rtcio
|
||||
.touch_pad5
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
13 => {
|
||||
rtcio
|
||||
.touch_pad4
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
14 => {
|
||||
rtcio
|
||||
.touch_pad6
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
15 => {
|
||||
rtcio
|
||||
.touch_pad3
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
25 => {
|
||||
rtcio.pad_dac1.modify(|r, w| unsafe {
|
||||
w.bits(r.bits())
|
||||
.pdac1_rue()
|
||||
.bit(pull_up)
|
||||
.pdac1_rde()
|
||||
.bit(pull_down)
|
||||
});
|
||||
}
|
||||
26 => {
|
||||
rtcio.pad_dac2.modify(|r, w| unsafe {
|
||||
w.bits(r.bits())
|
||||
.pdac2_rue()
|
||||
.bit(pull_up)
|
||||
.pdac2_rde()
|
||||
.bit(pull_down)
|
||||
});
|
||||
}
|
||||
27 => {
|
||||
rtcio
|
||||
.touch_pad7
|
||||
.modify(|r, w| unsafe { w.bits(r.bits()).rue().bit(pull_up).rde().bit(pull_down) });
|
||||
}
|
||||
32 => {
|
||||
rtcio.xtal_32k_pad.modify(|r, w| unsafe {
|
||||
w.bits(r.bits())
|
||||
.x32n_rue()
|
||||
.bit(pull_up)
|
||||
.x32n_rde()
|
||||
.bit(pull_down)
|
||||
});
|
||||
}
|
||||
33 => {
|
||||
rtcio.xtal_32k_pad.modify(|r, w| unsafe {
|
||||
w.bits(r.bits())
|
||||
.x32p_rue()
|
||||
.bit(pull_up)
|
||||
.x32p_rde()
|
||||
.bit(pull_down)
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
crate::gpio::gpio! {
|
||||
(0, 0, InputOutputAnalog (5 => EMAC_TX_CLK) (1 => CLK_OUT1))
|
||||
(1, 0, InputOutput (5 => EMAC_RXD2) (0 => U0TXD 1 => CLK_OUT3))
|
||||
(2, 0, InputOutputAnalog (1 => HSPIWP 3 => HS2_DATA0 4 => SD_DATA0) (3 => HS2_DATA0 4 => SD_DATA0))
|
||||
(3, 0, InputOutput (0 => U0RXD) (1 => CLK_OUT2))
|
||||
(4, 0, InputOutput (1 => HSPIHD 3 => HS2_DATA1 4 => SD_DATA1 5 => EMAC_TX_ER) (3 => HS2_DATA1 4 => SD_DATA1))
|
||||
(5, 0, InputOutput (1 => VSPICS0 3 => HS1_DATA6 5 => EMAC_RX_CLK) (3 => HS1_DATA6))
|
||||
(6, 0, InputOutput (4 => U1CTS) (0 => SD_CLK 1 => SPICLK 3 => HS1_CLK))
|
||||
(7, 0, InputOutput (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0) (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0 4 => U2RTS))
|
||||
(8, 0, InputOutput (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1 4 => U2CTS) (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1))
|
||||
(9, 0, InputOutput (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2 4 => U1RXD) (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2))
|
||||
(10, 0, InputOutput ( 0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3) (0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3 4 => U1TXD))
|
||||
(11, 0, InputOutput ( 1 => SPICS0) (0 => SD_CMD 1 => SPICS0 3 => HS1_CMD 4 => U1RTS))
|
||||
(12, 0, InputOutputAnalog (0 => MTDI 1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2) (1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2 5 => EMAC_TXD3))
|
||||
(13, 0, InputOutputAnalog (0 => MTCK 1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3) (1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3 5 => EMAC_RX_ER))
|
||||
(14, 0, InputOutputAnalog (0 => MTMS 1 => HSPICLK) (1 => HSPICLK 3 => HS2_CLK 4 => SD_CLK 5 => EMAC_TXD2))
|
||||
(15, 0, InputOutputAnalog (1 => HSPICS0 5 => EMAC_RXD3) (0 => MTDO 1 => HSPICS0 3 => HS2_CMD 4 => SD_CMD))
|
||||
(16, 0, InputOutput (3 => HS1_DATA4 4 => U2RXD) (3 => HS1_DATA4 5 => EMAC_CLK_OUT))
|
||||
(17, 0, InputOutput (3 => HS1_DATA5) (3 => HS1_DATA5 4 => U2TXD 5 => EMAC_CLK_180))
|
||||
(18, 0, InputOutput (1 => VSPICLK 3 => HS1_DATA7) (1 => VSPICLK 3 => HS1_DATA7))
|
||||
(19, 0, InputOutput (1 => VSPIQ 3 => U0CTS) (1 => VSPIQ 5 => EMAC_TXD0))
|
||||
(20, 0, InputOutput)
|
||||
(21, 0, InputOutput (1 => VSPIHD) (1 => VSPIHD 5 => EMAC_TX_EN))
|
||||
(22, 0, InputOutput (1 => VSPIWP) (1 => VSPIWP 3 => U0RTS 5 => EMAC_TXD1))
|
||||
(23, 0, InputOutput (1 => VSPID) (1 => VSPID 3 => HS1_STROBE))
|
||||
(24, 0, InputOutput)
|
||||
(25, 0, InputOutputAnalog (5 => EMAC_RXD0) ())
|
||||
(26, 0, InputOutputAnalog (5 => EMAC_RXD1) ())
|
||||
(27, 0, InputOutputAnalog (5 => EMAC_RX_DV) ())
|
||||
(32, 1, InputOutputAnalog)
|
||||
(33, 1, InputOutputAnalog)
|
||||
(34, 1, InputOnlyAnalog)
|
||||
(35, 1, InputOnlyAnalog)
|
||||
(36, 1, InputOnlyAnalog)
|
||||
(37, 1, InputOnlyAnalog)
|
||||
(38, 1, InputOnlyAnalog)
|
||||
(39, 1, InputOnlyAnalog)
|
||||
}
|
||||
|
||||
crate::gpio::analog! {
|
||||
(36, 0, sensor_pads, sense1_mux_sel, sense1_fun_sel, sense1_fun_ie)
|
||||
(37, 1, sensor_pads, sense2_mux_sel, sense2_fun_sel, sense2_fun_ie)
|
||||
(38, 2, sensor_pads, sense3_mux_sel, sense3_fun_sel, sense3_fun_ie)
|
||||
(39, 3, sensor_pads, sense4_mux_sel, sense4_fun_sel, sense4_fun_ie)
|
||||
(34, 4, adc_pad, adc1_mux_sel, adc1_fun_sel, adc1_fun_ie)
|
||||
(35, 5, adc_pad, adc2_mux_sel, adc2_fun_sel, adc1_fun_ie)
|
||||
(25, 6, pad_dac1, pdac1_mux_sel, pdac1_fun_sel, pdac1_fun_ie, pdac1_rue, pdac1_rde)
|
||||
(26, 7, pad_dac2, pdac2_mux_sel, pdac2_fun_sel, pdac2_fun_ie, pdac2_rue, pdac2_rde)
|
||||
(33, 8, xtal_32k_pad, x32n_mux_sel, x32n_fun_sel, x32n_fun_ie, x32n_rue, x32n_rde )
|
||||
(32, 9, xtal_32k_pad, x32p_mux_sel, x32p_fun_sel, x32p_fun_ie, x32p_rue, x32p_rde )
|
||||
(4, 10, touch_pad0, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(0, 11, touch_pad1, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(2, 12, touch_pad2, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(15, 13, touch_pad3, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(13, 14, touch_pad4, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(12, 15, touch_pad5, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(14, 16, touch_pad6, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
(27, 17, touch_pad7, mux_sel, fun_sel, fun_ie, rue, rde )
|
||||
}
|
||||
|
||||
impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank0 {
|
||||
fn pro_cpu_interrupt_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.pcpu_int.read().bits()
|
||||
}
|
||||
|
||||
fn pro_cpu_nmi_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.pcpu_nmi_int.read().bits()
|
||||
}
|
||||
|
||||
fn app_cpu_interrupt_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.acpu_int.read().bits()
|
||||
}
|
||||
|
||||
fn app_cpu_nmi_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.acpu_nmi_int.read().bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptStatusRegisterAccess for InterruptStatusRegisterAccessBank1 {
|
||||
fn pro_cpu_interrupt_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.pcpu_int1.read().bits()
|
||||
}
|
||||
|
||||
fn pro_cpu_nmi_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.pcpu_nmi_int1.read().bits()
|
||||
}
|
||||
|
||||
fn app_cpu_interrupt_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.acpu_int1.read().bits()
|
||||
}
|
||||
|
||||
fn app_cpu_nmi_status_read() -> u32 {
|
||||
unsafe { &*GPIO::PTR }.acpu_nmi_int1.read().bits()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user