mirror of
https://github.com/pascallanger/DIY-Multiprotocol-TX-Module.git
synced 2025-11-25 21:39:39 +00:00
Compare commits
2102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a998d2dc5 | ||
|
|
7927bc601e | ||
|
|
f8cc913898 | ||
|
|
a5428bc180 | ||
|
|
c0285f81f0 | ||
|
|
600049898a | ||
|
|
0e1db8f06b | ||
|
|
ec83f1e5a3 | ||
|
|
f8f7769d9c | ||
|
|
4dacac29c6 | ||
|
|
593f3b87f1 | ||
|
|
bb70116df0 | ||
|
|
d7c24d62ed | ||
|
|
07df1a8959 | ||
|
|
ca9e2870ab | ||
|
|
99ca6fa36c | ||
|
|
3d82560f71 | ||
|
|
44d362aeb4 | ||
|
|
3d4ed5ad11 | ||
|
|
ca3c2bbdc8 | ||
|
|
90c226c748 | ||
|
|
b757283f41 | ||
|
|
b3132c7bf1 | ||
|
|
9ba56f040a | ||
|
|
ac71b4d981 | ||
|
|
6bb2c06cf6 | ||
|
|
2ad3c727fd | ||
|
|
d36af55b84 | ||
|
|
f19bb05c7a | ||
|
|
08ea8818fb | ||
|
|
59c541d013 | ||
|
|
37e2b5f41c | ||
|
|
614a330cf2 | ||
|
|
9106548859 | ||
|
|
2f07e79e50 | ||
|
|
2233f6f862 | ||
|
|
ae842f0bdc | ||
|
|
4b36656246 | ||
|
|
ea282489f9 | ||
|
|
6f2d9430d0 | ||
|
|
bccd9dc0d9 | ||
|
|
af919fb940 | ||
|
|
7dab0de3c5 | ||
|
|
2c0525421a | ||
|
|
0844ec2efd | ||
|
|
49c3af12f9 | ||
|
|
96d335f458 | ||
|
|
eae412fe41 | ||
|
|
0270420531 | ||
|
|
93bb117961 | ||
|
|
29d1fb00b1 | ||
|
|
5a49d99c4f | ||
|
|
d66bf1a5b3 | ||
|
|
b41dccef67 | ||
|
|
addf2c5143 | ||
|
|
ff829f1f7b | ||
|
|
7ee72976c4 | ||
|
|
dc18e8e5b2 | ||
|
|
97de2aaf3d | ||
|
|
3b8b2ef376 | ||
|
|
d496f62719 | ||
|
|
fc978f95ef | ||
|
|
596e8c9b55 | ||
|
|
0cf58c2990 | ||
|
|
6ba1c8b118 | ||
|
|
b949ac0fed | ||
|
|
510f892d2f | ||
|
|
17750e774a | ||
|
|
be1591c489 | ||
|
|
3b22705166 | ||
|
|
c05c0a5693 | ||
|
|
d6fc6a3517 | ||
|
|
7bfaee8b4f | ||
|
|
89c00e8f17 | ||
|
|
447a58966f | ||
|
|
74162aa972 | ||
|
|
78a1e61f7a | ||
|
|
a917227ddc | ||
|
|
c866d07743 | ||
|
|
43d969f962 | ||
|
|
3973b42f81 | ||
|
|
6046ad81db | ||
|
|
63a9874aea | ||
|
|
89420fe2d4 | ||
|
|
9769354989 | ||
|
|
580b996215 | ||
|
|
8f5fb5083f | ||
|
|
821076ce95 | ||
|
|
0bfe1f8e63 | ||
|
|
9c35f6f73c | ||
|
|
983debe6ce | ||
|
|
66704b5a13 | ||
|
|
f11da0c1de | ||
|
|
7676398aab | ||
|
|
9c6c55fa00 | ||
|
|
5b9ca3ba06 | ||
|
|
23141b6087 | ||
|
|
6d4b4bd2c0 | ||
|
|
5cec22a757 | ||
|
|
153ba2e090 | ||
|
|
21e8bed52b | ||
|
|
ecfc5b0313 | ||
|
|
2bcebbda45 | ||
|
|
52e8d87ab1 | ||
|
|
b613345da4 | ||
|
|
f2665ca786 | ||
|
|
e3189d3aed | ||
|
|
c829e1c86f | ||
|
|
8499a508f5 | ||
|
|
50dec0e55d | ||
|
|
6f419adb7f | ||
|
|
502e8beafb | ||
|
|
5aae065dc0 | ||
|
|
152dbed3fa | ||
|
|
b516bb8d20 | ||
|
|
30e3e84066 | ||
|
|
8338104266 | ||
|
|
00c6aa52b9 | ||
|
|
e5689d2f1b | ||
|
|
b51dedcea1 | ||
|
|
49f004e53f | ||
|
|
062fc05eac | ||
|
|
6d080d5d5f | ||
|
|
0955340a93 | ||
|
|
9b2318cc7e | ||
|
|
2098cdc5e0 | ||
|
|
d19b5187c5 | ||
|
|
2a78b1d6b7 | ||
|
|
84ae6366eb | ||
|
|
e5b235ac83 | ||
|
|
0195384592 | ||
|
|
0937f832fc | ||
|
|
c77b4af2a0 | ||
|
|
b943bae8dd | ||
|
|
4955978eb6 | ||
|
|
df409fddf5 | ||
|
|
0330c596e4 | ||
|
|
8c6c58f12f | ||
|
|
4bc08d22b8 | ||
|
|
96fb3b20b7 | ||
|
|
47f713c6c8 | ||
|
|
443c7a6b99 | ||
|
|
e79ca9b7d7 | ||
|
|
b94f774f80 | ||
|
|
5614e8bef6 | ||
|
|
4ce3a5d298 | ||
|
|
f6de3de78c | ||
|
|
8099018132 | ||
|
|
360dde2e1b | ||
|
|
cc7b7638d3 | ||
|
|
321e4aee34 | ||
|
|
f18847df57 | ||
|
|
a2559a65d3 | ||
|
|
5ac41fdd15 | ||
|
|
6d38dd2d7a | ||
|
|
667058269c | ||
|
|
ad6f934892 | ||
|
|
714220c349 | ||
|
|
4887fca873 | ||
|
|
cfe80edcb6 | ||
|
|
484588ff6b | ||
|
|
1bb059c2a2 | ||
|
|
ef5d9cb6b3 | ||
|
|
37a06c050d | ||
|
|
5f0ed395ba | ||
|
|
ae27c8b671 | ||
|
|
088bfb9c2f | ||
|
|
b01462e36b | ||
|
|
abd36dc6a4 | ||
|
|
ebb8a33c1a | ||
|
|
2b0f663482 | ||
|
|
956e632392 | ||
|
|
90b6bb8f7d | ||
|
|
6153e84abf | ||
|
|
16357f29e9 | ||
|
|
0b8a5a7539 | ||
|
|
6874e3a6a7 | ||
|
|
96263ed8a6 | ||
|
|
7bb1cb9ae3 | ||
|
|
20e32c4cb0 | ||
|
|
b4421306c0 | ||
|
|
e53f723fdb | ||
|
|
832a331437 | ||
|
|
12e66bd84f | ||
|
|
d290cc519f | ||
|
|
f4b19fe33e | ||
|
|
767d2c079a | ||
|
|
7750310be5 | ||
|
|
6de4e1e1dd | ||
|
|
627d47f139 | ||
|
|
36675cf729 | ||
|
|
33d8234eb3 | ||
|
|
347eb11328 | ||
|
|
5ed61f30e1 | ||
|
|
a633f46f4f | ||
|
|
baf9a0f978 | ||
|
|
c98b8fc8bf | ||
|
|
2fae0a35b8 | ||
|
|
b04f38ed3d | ||
|
|
60d5dd4101 | ||
|
|
75b7dd1dd7 | ||
|
|
aa2717d6ab | ||
|
|
4058f04b39 | ||
|
|
25ebd55c85 | ||
|
|
d00b58c8ed | ||
|
|
e7eb07a5a4 | ||
|
|
a4cac50261 | ||
|
|
4f8da64822 | ||
|
|
4f89721cd0 | ||
|
|
43c2843490 | ||
|
|
c152265284 | ||
|
|
b591b92b4e | ||
|
|
090388aa1b | ||
|
|
c6ab696949 | ||
|
|
df45a3ff83 | ||
|
|
fedd04b724 | ||
|
|
d8630da09d | ||
|
|
6192f7287e | ||
|
|
5b8a08ab22 | ||
|
|
313b03fb84 | ||
|
|
5639def6fb | ||
|
|
15a254879c | ||
|
|
5baa9bd256 | ||
|
|
d5346c0eaf | ||
|
|
a2213fd6dc | ||
|
|
9b07f12f90 | ||
|
|
c9b49483e0 | ||
|
|
e2972a5823 | ||
|
|
d5b3ed832d | ||
|
|
9fcc030c15 | ||
|
|
fc81b7ee5a | ||
|
|
f168abc2bb | ||
|
|
fe6778635e | ||
|
|
65a6c19d02 | ||
|
|
a32b5561a1 | ||
|
|
4b4393952e | ||
|
|
ea205b1e69 | ||
|
|
748140cdee | ||
|
|
e8037c857f | ||
|
|
15e37cefee | ||
|
|
b1e4daf1c2 | ||
|
|
4e0fccfc63 | ||
|
|
48b90029c4 | ||
|
|
8b189af2f9 | ||
|
|
030cdd35a2 | ||
|
|
3789998ba9 | ||
|
|
ea24ab6032 | ||
|
|
1408431649 | ||
|
|
6810372064 | ||
|
|
708e2ac5f6 | ||
|
|
2178f6761d | ||
|
|
aa5fd82004 | ||
|
|
660282db2e | ||
|
|
fdd357619b | ||
|
|
5f12f99761 | ||
|
|
3d98abb6d4 | ||
|
|
e35879a5d0 | ||
|
|
37138f03ae | ||
|
|
51d39bbd8c | ||
|
|
0932a1c93f | ||
|
|
19164521e4 | ||
|
|
df28cfe3cc | ||
|
|
354878d542 | ||
|
|
695264d59a | ||
|
|
de190f3349 | ||
|
|
b049385094 | ||
|
|
75dc616130 | ||
|
|
b542f5e7cd | ||
|
|
f3bee3cded | ||
|
|
a9b7ab9a06 | ||
|
|
ed019e954e | ||
|
|
65186b4356 | ||
|
|
e1d4f9a270 | ||
|
|
69c95ca153 | ||
|
|
e6976fb08d | ||
|
|
f502ba3659 | ||
|
|
e6ccc7e7cc | ||
|
|
da9d8851c2 | ||
|
|
ad48291d2a | ||
|
|
d6da230369 | ||
|
|
f8ac406a94 | ||
|
|
e691ecd167 | ||
|
|
930c26a111 | ||
|
|
64419a6cf4 | ||
|
|
5c59cddc7a | ||
|
|
4c7a51be46 | ||
|
|
7dad0fb89f | ||
|
|
adebb3fc5c | ||
|
|
5ab00b9d18 | ||
|
|
fbd5d7cf48 | ||
|
|
84132678cc | ||
|
|
78421748ba | ||
|
|
7112f58dae | ||
|
|
e56f737b34 | ||
|
|
49d993f613 | ||
|
|
63dd8a9215 | ||
|
|
d5f819dd59 | ||
|
|
a68787f16e | ||
|
|
858ef5801c | ||
|
|
9e0bd29cee | ||
|
|
15395de579 | ||
|
|
db4aad04a7 | ||
|
|
466e4cf227 | ||
|
|
05a3780c38 | ||
|
|
85ea91cdbb | ||
|
|
985d7a6fd9 | ||
|
|
2a19b8dd45 | ||
|
|
b2b3078861 | ||
|
|
2ac92f5725 | ||
|
|
e2f5afd71e | ||
|
|
e094ee036d | ||
|
|
2ad7f3e9f2 | ||
|
|
8e1f2258f8 | ||
|
|
5c01bbf284 | ||
|
|
647425fc1a | ||
|
|
7286049d07 | ||
|
|
ce67a065cd | ||
|
|
8948cb6287 | ||
|
|
4daa5fa2bb | ||
|
|
c49a7dae0a | ||
|
|
890a042a43 | ||
|
|
c95e576ef3 | ||
|
|
2aa96dd129 | ||
|
|
908634474b | ||
|
|
79b525ee71 | ||
|
|
872b8259ab | ||
|
|
a14c82708f | ||
|
|
7e53778680 | ||
|
|
210fbe3b9e | ||
|
|
0a5fd72bdc | ||
|
|
6e1701ecc5 | ||
|
|
a5f627a2d6 | ||
|
|
b4a1f175c6 | ||
|
|
e0690fa661 | ||
|
|
bd962eff35 | ||
|
|
b515355249 | ||
|
|
d1feef97be | ||
|
|
f52f96d44e | ||
|
|
944ec62f49 | ||
|
|
30905014d2 | ||
|
|
32dbdfc6e3 | ||
|
|
b2e312b41e | ||
|
|
52f4096197 | ||
|
|
c547ea0c0f | ||
|
|
c73ee61128 | ||
|
|
90b287f1f4 | ||
|
|
0316c9eea9 | ||
|
|
374b46966c | ||
|
|
3705415927 | ||
|
|
bff68f482e | ||
|
|
32ed758072 | ||
|
|
5ce99ee419 | ||
|
|
ceea384a36 | ||
|
|
fd3b026e12 | ||
|
|
7e451c13a8 | ||
|
|
3dcf74c2e4 | ||
|
|
50f1eca4be | ||
|
|
0d97af5ae2 | ||
|
|
aeb8d67219 | ||
|
|
1f65025036 | ||
|
|
8df3687684 | ||
|
|
adf59a9d0d | ||
|
|
b9f00bdbc5 | ||
|
|
a10e169573 | ||
|
|
317b9a8156 | ||
|
|
1c632d462f | ||
|
|
c46b49ccf1 | ||
|
|
e70708b133 | ||
|
|
62486c2220 | ||
|
|
cffe66747a | ||
|
|
b31bbfa04f | ||
|
|
48e4cad3ad | ||
|
|
53f58ce2e1 | ||
|
|
eb8b5eac01 | ||
|
|
02008a8b2e | ||
|
|
5b82599eb9 | ||
|
|
a5e4b2c6fa | ||
|
|
987753ff73 | ||
|
|
ee080839b1 | ||
|
|
4290c75478 | ||
|
|
cc6be6027d | ||
|
|
4cfde0a80a | ||
|
|
a77aee0e1a | ||
|
|
6f36473975 | ||
|
|
f5720d38bb | ||
|
|
23478d3d21 | ||
|
|
ba72b6dedd | ||
|
|
103f595891 | ||
|
|
957d623b4b | ||
|
|
2be757e609 | ||
|
|
c4be660a05 | ||
|
|
c1c5f9fe3a | ||
|
|
53c0637a85 | ||
|
|
4ae30dc3b0 | ||
|
|
fc5fbc9899 | ||
|
|
2397bf365b | ||
|
|
42cd17d5f2 | ||
|
|
a35e01bbeb | ||
|
|
b21e8030b3 | ||
|
|
0984a42fe5 | ||
|
|
ed50d60108 | ||
|
|
a7f72a73e5 | ||
|
|
1c02cb46f5 | ||
|
|
da8fd21177 | ||
|
|
7e5cd9819a | ||
|
|
00aecb3ab1 | ||
|
|
cde77a88fd | ||
|
|
08a555f187 | ||
|
|
4039cbf8af | ||
|
|
3f652fa06c | ||
|
|
272d2be3ae | ||
|
|
7e461344a8 | ||
|
|
8af985a2cb | ||
|
|
08eee34446 | ||
|
|
0a5b97a177 | ||
|
|
cab782b38e | ||
|
|
d1518d763b | ||
|
|
44a676b809 | ||
|
|
d66709ea87 | ||
|
|
358a77cf7c | ||
|
|
1a631908f4 | ||
|
|
9f32a1f22b | ||
|
|
dfd3386319 | ||
|
|
3df836e6b8 | ||
|
|
2b8ed25843 | ||
|
|
62250d2f25 | ||
|
|
a4e9082f53 | ||
|
|
7217e8c41d | ||
|
|
a7ac093753 | ||
|
|
5503502bad | ||
|
|
5124c2a96d | ||
|
|
73d7728e08 | ||
|
|
8b7bd00a48 | ||
|
|
68a6af0eb5 | ||
|
|
693f9f58eb | ||
|
|
4a01e2d472 | ||
|
|
e4fd1f4399 | ||
|
|
0d5fcb0849 | ||
|
|
2236c256ba | ||
|
|
945ad2e7bd | ||
|
|
6f9740f03f | ||
|
|
f9fa4dff73 | ||
|
|
77bf17967d | ||
|
|
7281c0b5bf | ||
|
|
e6cab65560 | ||
|
|
cc115323e1 | ||
|
|
58665ea7a7 | ||
|
|
3920644caf | ||
|
|
25aecbf15e | ||
|
|
e18d8868d2 | ||
|
|
c6e5d00a2b | ||
|
|
7a5b4dea1a | ||
|
|
5df877f32c | ||
|
|
c5c7dda2e0 | ||
|
|
492b9e5ed4 | ||
|
|
9f721c528d | ||
|
|
7f3c80c2a9 | ||
|
|
8f789607e4 | ||
|
|
6a9b6ed4be | ||
|
|
953a97dae4 | ||
|
|
86778c5997 | ||
|
|
8c32cdf5fd | ||
|
|
d092593e5c | ||
|
|
edbf4b6908 | ||
|
|
43f688d011 | ||
|
|
054c3088c3 | ||
|
|
0cedd5bb66 | ||
|
|
1961579fe4 | ||
|
|
6906f1652e | ||
|
|
e2bbe8a422 | ||
|
|
8ea4e00d31 | ||
|
|
917e27280f | ||
|
|
ec92edfc85 | ||
|
|
afd2be6c59 | ||
|
|
a23d50bf0d | ||
|
|
cace1144db | ||
|
|
0d646ed1a6 | ||
|
|
e9b09ffecd | ||
|
|
0008633d6e | ||
|
|
396c005b0a | ||
|
|
d3c3fac4f7 | ||
|
|
cf4acc1d4c | ||
|
|
5bd95f8414 | ||
|
|
a31d9a83a3 | ||
|
|
9b24589897 | ||
|
|
8f3d634132 | ||
|
|
5ef1ccb99b | ||
|
|
e7d91bc76a | ||
|
|
e4309824c2 | ||
|
|
cc6a35ac8a | ||
|
|
1f13a6c281 | ||
|
|
69519bdf14 | ||
|
|
2e5a8f384a | ||
|
|
c803eeb26a | ||
|
|
6a7497cdf8 | ||
|
|
3067ea3a5c | ||
|
|
9a5309d84b | ||
|
|
fff18f825a | ||
|
|
e70bdd4152 | ||
|
|
cf77a1981f | ||
|
|
10e33f5d5c | ||
|
|
db442d81dd | ||
|
|
f800b4f90b | ||
|
|
712f297d86 | ||
|
|
0afed7d3a4 | ||
|
|
6a03972ff3 | ||
|
|
0e40f64c6b | ||
|
|
c83b769f28 | ||
|
|
dad3282bbb | ||
|
|
1621263fb0 | ||
|
|
2686cd0c48 | ||
|
|
b07b081a15 | ||
|
|
c5d4e8c191 | ||
|
|
928641f535 | ||
|
|
c4c5ffec4f | ||
|
|
c048e97d3a | ||
|
|
00f0719659 | ||
|
|
0482627512 | ||
|
|
5856442e0f | ||
|
|
7b281e47d6 | ||
|
|
6ef2934c18 | ||
|
|
19b931223b | ||
|
|
ca15d7108f | ||
|
|
e6e4d33847 | ||
|
|
815cf4fd99 | ||
|
|
5cf2bf2cf5 | ||
|
|
6632da0276 | ||
|
|
9b499ab7d1 | ||
|
|
d29461607b | ||
|
|
243bdf2240 | ||
|
|
cde185901a | ||
|
|
a9f35f8095 | ||
|
|
63d7e32e06 | ||
|
|
400fdb3cc6 | ||
|
|
9f1bdc901c | ||
|
|
61970b028e | ||
|
|
aee92737ca | ||
|
|
0fddd9c119 | ||
|
|
b156f66146 | ||
|
|
63e6590b65 | ||
|
|
103be7f6e0 | ||
|
|
2154b75499 | ||
|
|
6a83cb5577 | ||
|
|
70ce8e9a1f | ||
|
|
fdd0a00d5a | ||
|
|
a8cad1e70a | ||
|
|
40afd67fc6 | ||
|
|
f3b9206d67 | ||
|
|
b76dd4c8ac | ||
|
|
3eb507ebe6 | ||
|
|
fe0a1f0f94 | ||
|
|
fab3339b8d | ||
|
|
10604cf820 | ||
|
|
e297310a25 | ||
|
|
d71006f2ae | ||
|
|
7a8e099a79 | ||
|
|
822aa84fa2 | ||
|
|
948ce9e8b8 | ||
|
|
caf145c38a | ||
|
|
f964cdec98 | ||
|
|
6848fab873 | ||
|
|
72d57cae71 | ||
|
|
a23178e20e | ||
|
|
5ae4f0288b | ||
|
|
e0e51eb187 | ||
|
|
840944ea94 | ||
|
|
31ff27b1d3 | ||
|
|
a234ccbd05 | ||
|
|
6dfd54b8be | ||
|
|
edb7729b35 | ||
|
|
d434e63c22 | ||
|
|
cd7ede006c | ||
|
|
d4d0dc3dbc | ||
|
|
5c7f997e7a | ||
|
|
1623a007a4 | ||
|
|
0b19fa0bdf | ||
|
|
897c8b6ec5 | ||
|
|
d427a7fec1 | ||
|
|
b0749a1bee | ||
|
|
2406080515 | ||
|
|
ff96146b04 | ||
|
|
e3a5b2825d | ||
|
|
9fd72b5ad5 | ||
|
|
c2bf63991f | ||
|
|
a75ae7b65a | ||
|
|
507e4cb07b | ||
|
|
0f250bb04b | ||
|
|
4a315057a2 | ||
|
|
c8f44a0c4b | ||
|
|
382d853fe2 | ||
|
|
a5996c488e | ||
|
|
760642ecb3 | ||
|
|
c7a3548df0 | ||
|
|
d8acc6a5e8 | ||
|
|
431808286b | ||
|
|
747fa19259 | ||
|
|
bf61295b76 | ||
|
|
49a1ecea00 | ||
|
|
11f0e55bb1 | ||
|
|
331da37840 | ||
|
|
032e0641a5 | ||
|
|
f3d2ab61e4 | ||
|
|
e8b5f071fe | ||
|
|
ada72d6113 | ||
|
|
c2404d4f0d | ||
|
|
821732bba9 | ||
|
|
b6df650f50 | ||
|
|
e783ce5788 | ||
|
|
18af4a0724 | ||
|
|
7e6ec1dc8d | ||
|
|
4fbe0859e0 | ||
|
|
b89c23fe7c | ||
|
|
e038c49ae9 | ||
|
|
bed02c9384 | ||
|
|
96bc4f7cdf | ||
|
|
6503469ddd | ||
|
|
61a1c3742c | ||
|
|
1fb2a38bc1 | ||
|
|
22a0d79315 | ||
|
|
0cc72772a3 | ||
|
|
11f4e636e3 | ||
|
|
fef1a2e041 | ||
|
|
41a9c8e013 | ||
|
|
c1ad02b792 | ||
|
|
ab2315c951 | ||
|
|
7948e33cbc | ||
|
|
2be2dce584 | ||
|
|
69bdfe3dba | ||
|
|
d73d163a58 | ||
|
|
48a52ae5b2 | ||
|
|
078dc2ab17 | ||
|
|
6f4522caa6 | ||
|
|
3c76ce9f39 | ||
|
|
8601149051 | ||
|
|
d7ef15d435 | ||
|
|
27e3645b56 | ||
|
|
95eb4e1a22 | ||
|
|
32ea07bf5a | ||
|
|
06272575c5 | ||
|
|
edd6432d4c | ||
|
|
303c4615e9 | ||
|
|
7d327c1622 | ||
|
|
6a74b83f98 | ||
|
|
c601c2dd98 | ||
|
|
c27b60749f | ||
|
|
b3cb286088 | ||
|
|
361903e8ec | ||
|
|
d72c69242d | ||
|
|
539819fa0c | ||
|
|
d80c218744 | ||
|
|
0603d220a9 | ||
|
|
e13fe56f47 | ||
|
|
acd7694485 | ||
|
|
7f50edacc8 | ||
|
|
5a4906c5b5 | ||
|
|
4e906757b9 | ||
|
|
1f0b21e351 | ||
|
|
2f3ea323c7 | ||
|
|
d4e77c6499 | ||
|
|
ffc56b049d | ||
|
|
ddac89d732 | ||
|
|
fecf2805c7 | ||
|
|
4dcc88ba32 | ||
|
|
084308d8a4 | ||
|
|
1ffb5c405b | ||
|
|
ad29409407 | ||
|
|
6f33abb25e | ||
|
|
2c9693389e | ||
|
|
d3c82c4da4 | ||
|
|
541a96e7b4 | ||
|
|
5b234a9cbc | ||
|
|
24c3a62f3a | ||
|
|
23756a387a | ||
|
|
efbd350dfd | ||
|
|
072e95c84e | ||
|
|
4be26e7202 | ||
|
|
a616cbb95c | ||
|
|
8aea9aa3dd | ||
|
|
e4992bc917 | ||
|
|
000b3e97c7 | ||
|
|
cb91e19413 | ||
|
|
c72e690085 | ||
|
|
f1a4e659e7 | ||
|
|
0d56d17d52 | ||
|
|
96c89c8c97 | ||
|
|
7df5a1f211 | ||
|
|
8ae8e028c7 | ||
|
|
581ad5f8f4 | ||
|
|
977bae3750 | ||
|
|
8a0161607e | ||
|
|
cd6a7bfc3a | ||
|
|
4671700b7d | ||
|
|
840ca74407 | ||
|
|
0d8a7e46de | ||
|
|
8b9940b0f0 | ||
|
|
521b819b8a | ||
|
|
208e9ef64b | ||
|
|
a7ea8e012e | ||
|
|
d940a7e49a | ||
|
|
9d50171034 | ||
|
|
701a5b9e28 | ||
|
|
d11785ef2d | ||
|
|
f4a4d91ef8 | ||
|
|
9514e47ed3 | ||
|
|
9461f2d632 | ||
|
|
f6064d03e1 | ||
|
|
0d917e0dc5 | ||
|
|
9408af8d08 | ||
|
|
862ab48bbc | ||
|
|
f498347c53 | ||
|
|
3f77f59c8a | ||
|
|
e96186015d | ||
|
|
1fa6e4526b | ||
|
|
3853d585a0 | ||
|
|
e8c6225ef0 | ||
|
|
17c67cc780 | ||
|
|
dcc4236833 | ||
|
|
6a7c735924 | ||
|
|
3c40cd853e | ||
|
|
5357660b68 | ||
|
|
a15a911f8e | ||
|
|
ded0487ce6 | ||
|
|
e7b079ece7 | ||
|
|
0cbe4de920 | ||
|
|
5c3bf30fe7 | ||
|
|
56b8694fd5 | ||
|
|
c9fecafada | ||
|
|
f3ca7ad644 | ||
|
|
c3b48c78af | ||
|
|
18dcc29a0d | ||
|
|
c3a5c263d3 | ||
|
|
ccdf60d525 | ||
|
|
443e1cec75 | ||
|
|
aa540514b8 | ||
|
|
fda7e2e5b6 | ||
|
|
fb4d5cf437 | ||
|
|
9fc2898a56 | ||
|
|
2e24323adf | ||
|
|
77abc1ed7c | ||
|
|
259d550d04 | ||
|
|
1a8f9c5a12 | ||
|
|
1916eb3095 | ||
|
|
fc3eec7ae1 | ||
|
|
8d10c9b004 | ||
|
|
511f77f5e7 | ||
|
|
a58d7a4d79 | ||
|
|
6748f6ce78 | ||
|
|
2dcb6b1987 | ||
|
|
2b4ac41142 | ||
|
|
2b47958207 | ||
|
|
5538d4d1c4 | ||
|
|
467756df48 | ||
|
|
534ebb26c0 | ||
|
|
21790a9725 | ||
|
|
6bc22c8ac4 | ||
|
|
ac1c422f14 | ||
|
|
efa1860104 | ||
|
|
c673b4254f | ||
|
|
96db173694 | ||
|
|
1d5be624fe | ||
|
|
56f9672896 | ||
|
|
303b3f08b8 | ||
|
|
150e3fd92e | ||
|
|
840b01273c | ||
|
|
8a7beda339 | ||
|
|
65606f7083 | ||
|
|
0095071ebf | ||
|
|
ae4ee9061d | ||
|
|
6ba06bc006 | ||
|
|
00f247eae1 | ||
|
|
c250c91ab4 | ||
|
|
b1ac10a4c4 | ||
|
|
af4185fbea | ||
|
|
aa85d4d797 | ||
|
|
74cc63e6dd | ||
|
|
04ed613ca3 | ||
|
|
9ea157b9b6 | ||
|
|
536d7f6124 | ||
|
|
869a01b57f | ||
|
|
ea982b23c2 | ||
|
|
15dfe8cead | ||
|
|
b50027150e | ||
|
|
103acb8294 | ||
|
|
aca23b8bca | ||
|
|
aaffbbc5be | ||
|
|
e0002c9488 | ||
|
|
687d6d914c | ||
|
|
9a61b2701e | ||
|
|
d8224ebefe | ||
|
|
cc2af73a05 | ||
|
|
aecd44ada6 | ||
|
|
72593c4d99 | ||
|
|
52ca258616 | ||
|
|
04412667ff | ||
|
|
4124ec80ec | ||
|
|
693ea75e95 | ||
|
|
9e3c1ab059 | ||
|
|
5fc860702f | ||
|
|
6ad2ee5a47 | ||
|
|
b0f64297cb | ||
|
|
f99a63dbc0 | ||
|
|
9261e8fe94 | ||
|
|
52bb116e24 | ||
|
|
7006c36101 | ||
|
|
f7d452e3f1 | ||
|
|
598e50a40f | ||
|
|
eadfa26c3b | ||
|
|
b03491fdf7 | ||
|
|
42911c63b3 | ||
|
|
43462757ed | ||
|
|
0eee1c5d15 | ||
|
|
3a21bf69f1 | ||
|
|
7d4c8f7f07 | ||
|
|
866d19d649 | ||
|
|
c27ec2475c | ||
|
|
1d64cbfaa0 | ||
|
|
5d1baa89e4 | ||
|
|
e846ce7e98 | ||
|
|
300b3582a6 | ||
|
|
3a7ffe62b0 | ||
|
|
d990ebe49e | ||
|
|
bd78739217 | ||
|
|
6c7312a09c | ||
|
|
1525e564cb | ||
|
|
7fb4bbbf3c | ||
|
|
2553d44d32 | ||
|
|
dc902b7a61 | ||
|
|
b992cbd92e | ||
|
|
22711fad28 | ||
|
|
a9df0abed3 | ||
|
|
fd65550f8d | ||
|
|
b910ad3386 | ||
|
|
fb9f094365 | ||
|
|
05470446a0 | ||
|
|
903982afb7 | ||
|
|
8bea5b125b | ||
|
|
c658a892f2 | ||
|
|
3f0d6cfcf1 | ||
|
|
c6657c199c | ||
|
|
cf6d980b8b | ||
|
|
c3ff49e86e | ||
|
|
e51f91f041 | ||
|
|
2589c67f6c | ||
|
|
2f4f19b52b | ||
|
|
b3ed7563dc | ||
|
|
ea96c328fc | ||
|
|
f42da14413 | ||
|
|
2149149723 | ||
|
|
f1916b3d0b | ||
|
|
10788976b6 | ||
|
|
567d26a385 | ||
|
|
220eb8f6f4 | ||
|
|
b40e7d6e97 | ||
|
|
97dd90a718 | ||
|
|
75ba6401a4 | ||
|
|
5cf8d8d263 | ||
|
|
058e09c413 | ||
|
|
63fb1b1f36 | ||
|
|
7c0b9ce3c6 | ||
|
|
c153d236f2 | ||
|
|
3b3a3c275c | ||
|
|
f4691d08cc | ||
|
|
e5d9471b33 | ||
|
|
ab9525c507 | ||
|
|
b1fb3a5470 | ||
|
|
b2194790eb | ||
|
|
8901310ed4 | ||
|
|
09c6adaa95 | ||
|
|
5fe1e9674e | ||
|
|
601116bd59 | ||
|
|
27dc02fdec | ||
|
|
03f417546e | ||
|
|
3b9aa79201 | ||
|
|
5aa4936c0e | ||
|
|
1e65d58113 | ||
|
|
e2a66bdd1f | ||
|
|
4f9c30505a | ||
|
|
1ec817c96c | ||
|
|
41528ae7b6 | ||
|
|
77d248df0b | ||
|
|
c6cb13b332 | ||
|
|
d67108fa8b | ||
|
|
b7447f055a | ||
|
|
3902ed19b7 | ||
|
|
51ba7cf75c | ||
|
|
82fb5dd0da | ||
|
|
2ac20cb575 | ||
|
|
3f33e5b2a8 | ||
|
|
655ea98bf1 | ||
|
|
238260612c | ||
|
|
cd26c2d8e6 | ||
|
|
814e28b86a | ||
|
|
f515ba79af | ||
|
|
e6f16700e4 | ||
|
|
f38e7c35da | ||
|
|
b3576b7162 | ||
|
|
d9914e95d9 | ||
|
|
64c79626b4 | ||
|
|
e047a76855 | ||
|
|
e7449897f9 | ||
|
|
db093cf25b | ||
|
|
4bb4bd9792 | ||
|
|
c16654c8cf | ||
|
|
817ff7e239 | ||
|
|
685de52538 | ||
|
|
1dc52773f4 | ||
|
|
fbb04ea88a | ||
|
|
23da318645 | ||
|
|
30b77931d7 | ||
|
|
39327b2b27 | ||
|
|
13c3af96a6 | ||
|
|
4411732022 | ||
|
|
0c08b8b93b | ||
|
|
838755aabf | ||
|
|
2bca104e33 | ||
|
|
cf857f3256 | ||
|
|
1647d3d841 | ||
|
|
2f5252ab88 | ||
|
|
eb89b5be1b | ||
|
|
e80fe29dba | ||
|
|
74ab2a4bd7 | ||
|
|
edd4f740e0 | ||
|
|
d0c9bc801b | ||
|
|
73dd63caa1 | ||
|
|
cc4703ea40 | ||
|
|
f105d7c7eb | ||
|
|
e85ad00a46 | ||
|
|
658dfa4290 | ||
|
|
df75036501 | ||
|
|
9ab5353122 | ||
|
|
5345ece322 | ||
|
|
9f09c81ef4 | ||
|
|
c8dea20865 | ||
|
|
bf09177916 | ||
|
|
d71640ad7e | ||
|
|
68afcc0c44 | ||
|
|
9b30a3d72a | ||
|
|
789cd21618 | ||
|
|
d9f343e20b | ||
|
|
0b8ff15133 | ||
|
|
27a4748111 | ||
|
|
18efaef38c | ||
|
|
35137b87ed | ||
|
|
9019c46102 | ||
|
|
52d0869c50 | ||
|
|
236b375669 | ||
|
|
9502b5a12a | ||
|
|
ad18856f7a | ||
|
|
9015b78aaf | ||
|
|
8cc7a4f693 | ||
|
|
ac298fee61 | ||
|
|
457fc5296e | ||
|
|
b8f3cd1ad8 | ||
|
|
f9f265271a | ||
|
|
dde5f6e119 | ||
|
|
add3fb13ef | ||
|
|
51c1936c24 | ||
|
|
07adeaf60d | ||
|
|
811913bf89 | ||
|
|
8906c85d06 | ||
|
|
7df5894ca4 | ||
|
|
fa9e559195 | ||
|
|
81f408ee77 | ||
|
|
aa8a059553 | ||
|
|
8c7918577d | ||
|
|
7219885196 | ||
|
|
9f8915239f | ||
|
|
83401f6e1a | ||
|
|
dd686ad9d1 | ||
|
|
7d04005c72 | ||
|
|
cdbaeb1c6c | ||
|
|
6bf432a112 | ||
|
|
f6ef136502 | ||
|
|
4a9d295f1a | ||
|
|
105e0bb7a4 | ||
|
|
ab27ee50b0 | ||
|
|
f4a4f67453 | ||
|
|
fb88eebc2f | ||
|
|
6fea0c2a93 | ||
|
|
301c8da9eb | ||
|
|
4facdf0932 | ||
|
|
ce489887db | ||
|
|
55e30bd4aa | ||
|
|
eb0d03d420 | ||
|
|
db1da2d9cf | ||
|
|
6fa3b8597f | ||
|
|
c8da89f0d3 | ||
|
|
c3c89801e9 | ||
|
|
eb3f8b0d91 | ||
|
|
d3285f002e | ||
|
|
e81ee38fa5 | ||
|
|
499e854275 | ||
|
|
cd235bbcde | ||
|
|
984aa3f413 | ||
|
|
11287cb9c0 | ||
|
|
5cc49a8862 | ||
|
|
fabda76e98 | ||
|
|
70db505dd9 | ||
|
|
29fd1c800b | ||
|
|
d70d8f62a4 | ||
|
|
bef562964d | ||
|
|
fc5495b6d1 | ||
|
|
f3838ae4b0 | ||
|
|
b3eccf55ba | ||
|
|
835cc3d0a2 | ||
|
|
b49584ec16 | ||
|
|
334e49c4ca | ||
|
|
3fc5be111b | ||
|
|
1bc2dd2964 | ||
|
|
68dcc75949 | ||
|
|
dc9f738f30 | ||
|
|
4171d2f93b | ||
|
|
64fb90960b | ||
|
|
93e277bb0f | ||
|
|
337216efac | ||
|
|
f1f5b68821 | ||
|
|
8f941e6d12 | ||
|
|
6a4dcbd6e1 | ||
|
|
7e773dfe1b | ||
|
|
465ae75ffc | ||
|
|
8da03940e8 | ||
|
|
f280779528 | ||
|
|
5c568da125 | ||
|
|
f46b8366b0 | ||
|
|
1d9c052c01 | ||
|
|
544927b9b7 | ||
|
|
8b5bb0d358 | ||
|
|
6031f1e3b8 | ||
|
|
c297df76ef | ||
|
|
122e9f4fca | ||
|
|
748ed01252 | ||
|
|
56188328fb | ||
|
|
880e463b01 | ||
|
|
a53046bf12 | ||
|
|
a8050fd8a1 | ||
|
|
6ae819e8d5 | ||
|
|
79c73444ab | ||
|
|
8cc1b07456 | ||
|
|
0d43614ac3 | ||
|
|
19f879da7f | ||
|
|
ed42bf311f | ||
|
|
fd4346cb64 | ||
|
|
6e458ebd4a | ||
|
|
0f0be60245 | ||
|
|
27783677c2 | ||
|
|
4418cab3a5 | ||
|
|
b3cddfe2d4 | ||
|
|
a19ac87a04 | ||
|
|
3ba951fd91 | ||
|
|
824f23c3d6 | ||
|
|
fc49a32008 | ||
|
|
5ca0d31606 | ||
|
|
ebd44d9628 | ||
|
|
329fa1c2a5 | ||
|
|
9a5bf7999b | ||
|
|
7debad6c67 | ||
|
|
dc9b84ea8b | ||
|
|
199c254838 | ||
|
|
7ff482dafe | ||
|
|
9376019198 | ||
|
|
cec654a75b | ||
|
|
3e004d01ec | ||
|
|
21266d0a17 | ||
|
|
2b62b8dab3 | ||
|
|
b24564ffe9 | ||
|
|
f57d436640 | ||
|
|
762613bd7f | ||
|
|
e2ed752b51 | ||
|
|
2610926f47 | ||
|
|
cf1a74e532 | ||
|
|
f55b451d9b | ||
|
|
ae51e389ca | ||
|
|
6f71ef1d03 | ||
|
|
94a9e4db05 | ||
|
|
82dc0c4f32 | ||
|
|
bb5827629e | ||
|
|
76f7cb7126 | ||
|
|
d5005323d4 | ||
|
|
9bc1a9963f | ||
|
|
cefbf2ee70 | ||
|
|
e10dc6dcec | ||
|
|
e4f4d278a8 | ||
|
|
3dd9df558e | ||
|
|
87c2216d0f | ||
|
|
8cd5ff6bd6 | ||
|
|
28868e4c78 | ||
|
|
01bef23ac9 | ||
|
|
9379bd1792 | ||
|
|
63a479b6b6 | ||
|
|
49b0d92c75 | ||
|
|
277402a25d | ||
|
|
cc83177542 | ||
|
|
5a342cf8e6 | ||
|
|
7d5dabde82 | ||
|
|
e3917226cc | ||
|
|
4e3c1edd52 | ||
|
|
e52b7ea7ff | ||
|
|
6f60d87f85 | ||
|
|
049db615e3 | ||
|
|
e557155b17 | ||
|
|
9bf5b0c9a7 | ||
|
|
ea860f24a1 | ||
|
|
5e89d49bd9 | ||
|
|
cb1ba0e00c | ||
|
|
0f965b6800 | ||
|
|
486c2170cd | ||
|
|
83641a4f54 | ||
|
|
5cb2326ea7 | ||
|
|
84d7986353 | ||
|
|
a3c2628359 | ||
|
|
8ae34bdd9d | ||
|
|
4a9374936d | ||
|
|
110378e3b4 | ||
|
|
bd60feff60 | ||
|
|
2181d0b33c | ||
|
|
2afa7ea691 | ||
|
|
d0f76117cd | ||
|
|
e25060a16e | ||
|
|
5e6da326f6 | ||
|
|
981a8f8d2c | ||
|
|
5c7e592e1e | ||
|
|
2a25f76b56 | ||
|
|
41d579dc23 | ||
|
|
7d41017850 | ||
|
|
daeba0d43a | ||
|
|
24fd5ba361 | ||
|
|
3353637c8f | ||
|
|
9601e6942d | ||
|
|
4b32a93f27 | ||
|
|
9e8978166e | ||
|
|
6bf873f4db | ||
|
|
1318f1a29b | ||
|
|
d9282ac750 | ||
|
|
205d728798 | ||
|
|
bd9c772a52 | ||
|
|
76e9002995 | ||
|
|
e990d44bb6 | ||
|
|
2ffa2d7b73 | ||
|
|
0e4583f8e2 | ||
|
|
3802722bb1 | ||
|
|
41f0a712fd | ||
|
|
4633c37380 | ||
|
|
b779becf8c | ||
|
|
35089febab | ||
|
|
a95fd1e1d8 | ||
|
|
6535f64699 | ||
|
|
b15a5d4a45 | ||
|
|
65a5c37ffb | ||
|
|
6cf58c032c | ||
|
|
e0365df6dd | ||
|
|
40ae0c7257 | ||
|
|
aecd4c5266 | ||
|
|
113caf4970 | ||
|
|
da2d57c8e9 | ||
|
|
3aed8b8479 | ||
|
|
ff58a826b1 | ||
|
|
9a0873e404 | ||
|
|
7d9133793c | ||
|
|
f43d9883cf | ||
|
|
067842818e | ||
|
|
8ea70a1b77 | ||
|
|
47207dcead | ||
|
|
235876a4d8 | ||
|
|
af88abb13f | ||
|
|
f13701b120 | ||
|
|
5f7c598a34 | ||
|
|
defd354286 | ||
|
|
f9e6b30550 | ||
|
|
d451af365a | ||
|
|
57a78535f2 | ||
|
|
f824ecb7f9 | ||
|
|
98f848138d | ||
|
|
200a1dc3da | ||
|
|
507e1d2b50 | ||
|
|
0f752e2571 | ||
|
|
ca6cd65d46 | ||
|
|
ddc287a84e | ||
|
|
c92ec031ae | ||
|
|
ef2d4d6b12 | ||
|
|
2753f6c4e5 | ||
|
|
021577d638 | ||
|
|
dd3f8b4717 | ||
|
|
a1fb4a0ac5 | ||
|
|
ebf0c4fae6 | ||
|
|
44a086a27b | ||
|
|
4624075112 | ||
|
|
97a3c8dca1 | ||
|
|
c50b5d93d7 | ||
|
|
08f9176f3f | ||
|
|
f95ca6eac6 | ||
|
|
34165c0d90 | ||
|
|
1f87cf09d7 | ||
|
|
005c7b9cb0 | ||
|
|
2b265bd1de | ||
|
|
3e83216384 | ||
|
|
a9889b5b79 | ||
|
|
2aa5b7917f | ||
|
|
040354501c | ||
|
|
bd46f6d25f | ||
|
|
7e801269af | ||
|
|
2586e2e661 | ||
|
|
fb2a5c2378 | ||
|
|
f490fa6e7d | ||
|
|
df347be672 | ||
|
|
4e518bf70c | ||
|
|
29e90246d2 | ||
|
|
1e62c0d836 | ||
|
|
278ebf4ff2 | ||
|
|
edc58a3496 | ||
|
|
7c96862b19 | ||
|
|
11b18f7468 | ||
|
|
86787b6099 | ||
|
|
12645c19aa | ||
|
|
7e06a57fd9 | ||
|
|
4d7c31cfa1 | ||
|
|
f5e4fc5a3d | ||
|
|
1c4174431e | ||
|
|
60d4f2d6c7 | ||
|
|
5a30a96c0c | ||
|
|
09b454ba6a | ||
|
|
8ea06e84a6 | ||
|
|
ebaf56d964 | ||
|
|
517d2bb1ec | ||
|
|
706d43ee41 | ||
|
|
e537d27e28 | ||
|
|
948f28b0af | ||
|
|
70f7c6a25e | ||
|
|
a0a0cd4f57 | ||
|
|
dfa120339c | ||
|
|
36f194b9bd | ||
|
|
12dd851fc3 | ||
|
|
cc9a5893de | ||
|
|
9d2ac3f2c4 | ||
|
|
9cda61de3d | ||
|
|
060d93d68b | ||
|
|
8d1e8c6699 | ||
|
|
f4865c5206 | ||
|
|
2d44c89149 | ||
|
|
c2050d4314 | ||
|
|
7991605106 | ||
|
|
adeea85c9a | ||
|
|
c32e390184 | ||
|
|
154f61fb11 | ||
|
|
b08bcf041b | ||
|
|
e51f6e1e86 | ||
|
|
62e2a0211f | ||
|
|
5286e7f89c | ||
|
|
e81ba55967 | ||
|
|
25a2689bd0 | ||
|
|
c949eb5491 | ||
|
|
709c5cbd71 | ||
|
|
3ac59a5bc0 | ||
|
|
6894228a63 | ||
|
|
a7b914b84e | ||
|
|
84922e84a3 | ||
|
|
7ee0b2b0a3 | ||
|
|
5c06a241ac | ||
|
|
0cb0128caf | ||
|
|
8661b8074c | ||
|
|
fa52a1a81e | ||
|
|
680fa7a350 | ||
|
|
3850ce88d3 | ||
|
|
a800f1efc7 | ||
|
|
514bbbe8a3 | ||
|
|
700b922350 | ||
|
|
bdfeeb9a41 | ||
|
|
2b80d1a6d8 | ||
|
|
fa6f2061e7 | ||
|
|
ff5f12e4d2 | ||
|
|
9ad6e8142d | ||
|
|
49e3534738 | ||
|
|
4cb7ba83e9 | ||
|
|
9282828ffc | ||
|
|
2f2a2d4bee | ||
|
|
b904e6a889 | ||
|
|
804d5723c2 | ||
|
|
681e3a6865 | ||
|
|
dd75c56404 | ||
|
|
86cee1e339 | ||
|
|
063edc032a | ||
|
|
32c59cb583 | ||
|
|
0242f88d26 | ||
|
|
940a89fa38 | ||
|
|
364a66ad0b | ||
|
|
f714315b0d | ||
|
|
ed83471a13 | ||
|
|
1263aa7884 | ||
|
|
c21afc3f8a | ||
|
|
59af0594dc | ||
|
|
8cf6d4f7d8 | ||
|
|
61ceeb44fb | ||
|
|
995bb921c1 | ||
|
|
e7cab78aad | ||
|
|
1c06bd45ef | ||
|
|
85a3e112e0 | ||
|
|
9c5e054dda | ||
|
|
86932e6da4 | ||
|
|
5f94fdfc93 | ||
|
|
74584dd39e | ||
|
|
18ed5d51a9 | ||
|
|
d7076f5295 | ||
|
|
29a5397491 | ||
|
|
47a4261631 | ||
|
|
a3927218a6 | ||
|
|
7d846a3d0d | ||
|
|
8db1f6d1e2 | ||
|
|
9cb679d545 | ||
|
|
006d705cbd | ||
|
|
a19a14665f | ||
|
|
55bc6f9a00 | ||
|
|
997d31addd | ||
|
|
548390b0d7 | ||
|
|
bd64bdedc3 | ||
|
|
846e09c7cb | ||
|
|
2ba3552578 | ||
|
|
943bb6d15b | ||
|
|
af53e75ae4 | ||
|
|
14ca63571d | ||
|
|
10c5ce68f5 | ||
|
|
d9fb856eb8 | ||
|
|
f5c08158cb | ||
|
|
9ee0311ea9 | ||
|
|
69ed2d2428 | ||
|
|
0c5fae01b5 | ||
|
|
72052925a6 | ||
|
|
dc2d02bf32 | ||
|
|
ee267ea086 | ||
|
|
1e730f3376 | ||
|
|
c230c52af6 | ||
|
|
219beb4a0e | ||
|
|
c4807c44f3 | ||
|
|
7dafd14644 | ||
|
|
57c3e8c51d | ||
|
|
da71eca978 | ||
|
|
7a47af14be | ||
|
|
f67adc53ab | ||
|
|
68e9948493 | ||
|
|
894416c719 | ||
|
|
11650450c8 | ||
|
|
6973d85b10 | ||
|
|
bf5598515e | ||
|
|
cc03c27d17 | ||
|
|
9cd729df71 | ||
|
|
d6ee9fbfac | ||
|
|
b6999f5c67 | ||
|
|
c02f273d57 | ||
|
|
8d87bfb4a3 | ||
|
|
0a7cd3d6e9 | ||
|
|
235699b3b9 | ||
|
|
1aebfbecc1 | ||
|
|
00e47b9afb | ||
|
|
68b94cfcaa | ||
|
|
0068f21a2d | ||
|
|
7a7e639490 | ||
|
|
81bae1441e | ||
|
|
3516e5ae8a | ||
|
|
04bbe3187f | ||
|
|
877cdec4ea | ||
|
|
21ab94c512 | ||
|
|
70f52afb3d | ||
|
|
85f4dbd670 | ||
|
|
bee70e67bc | ||
|
|
7e137f17a5 | ||
|
|
c576f62ca3 | ||
|
|
9c8499e6cd | ||
|
|
f81e7cb78a | ||
|
|
773f3048f7 | ||
|
|
40faaea7f8 | ||
|
|
47b4409f3e | ||
|
|
9ce23c3d61 | ||
|
|
f40ffed069 | ||
|
|
0b223170d9 | ||
|
|
e61f8a512c | ||
|
|
65b43c39ed | ||
|
|
131645f31e | ||
|
|
ca8fc14d36 | ||
|
|
efe693c0c0 | ||
|
|
4eb2d073ac | ||
|
|
fd49c02e18 | ||
|
|
f0e3d492d3 | ||
|
|
3f8ba6be9d | ||
|
|
df4563379d | ||
|
|
f0f9d8faa7 | ||
|
|
031b4b637a | ||
|
|
e206ae3403 | ||
|
|
021c463d80 | ||
|
|
41d75da33f | ||
|
|
c7e7a559a6 | ||
|
|
4f5dfcc65b | ||
|
|
e85208110f | ||
|
|
17bb2f11e4 | ||
|
|
124db5f11f | ||
|
|
6b099d7298 | ||
|
|
7ac2e227b1 | ||
|
|
868c1aa251 | ||
|
|
7283fc89a5 | ||
|
|
f933560934 | ||
|
|
7ee918ad49 | ||
|
|
e30ebd39fd | ||
|
|
7ac6ff828c | ||
|
|
017f5f8e74 | ||
|
|
2c46feb8f8 | ||
|
|
f5171f3f47 | ||
|
|
1a2c28435a | ||
|
|
7221fc2060 | ||
|
|
e17e7ab4ec | ||
|
|
46eb0bc474 | ||
|
|
b03227ea9f | ||
|
|
a1de5e15c5 | ||
|
|
7f2322ba60 | ||
|
|
5c04844461 | ||
|
|
591a185fc2 | ||
|
|
fbc0b60b68 | ||
|
|
325ce8824e | ||
|
|
d54e25bb44 | ||
|
|
740943aee2 | ||
|
|
73ee93335b | ||
|
|
c69c3361ac | ||
|
|
a91d019738 | ||
|
|
dc272a472d | ||
|
|
b265108322 | ||
|
|
6cab6a9646 | ||
|
|
3baf237fa8 | ||
|
|
fa8503546e | ||
|
|
b9d988c712 | ||
|
|
2bbf672094 | ||
|
|
a6f8fe4c95 | ||
|
|
6d7415880a | ||
|
|
479e4588f3 | ||
|
|
821f1bdc2b | ||
|
|
5cdd672e84 | ||
|
|
48fb8d0b92 | ||
|
|
52fae655ba | ||
|
|
3ab6e84275 | ||
|
|
2b5ab6db11 | ||
|
|
a0f6d47153 | ||
|
|
787901ba73 | ||
|
|
37c7167128 | ||
|
|
35e1ddc578 | ||
|
|
d61e7b8729 | ||
|
|
8ef73f9dd2 | ||
|
|
f7448cc2e4 | ||
|
|
6af35b5ec5 | ||
|
|
8f4e5c6064 | ||
|
|
2d76775e40 | ||
|
|
8eb71cbe74 | ||
|
|
9146a8bb21 | ||
|
|
445bca7333 | ||
|
|
6286551ef5 | ||
|
|
1ae6a69c7a | ||
|
|
34851b772f | ||
|
|
4823aa378e | ||
|
|
40a42e86ee | ||
|
|
c518b52243 | ||
|
|
abccc3c3cd | ||
|
|
9786b7139e | ||
|
|
28e8e3434e | ||
|
|
cdf59fba10 | ||
|
|
be8571cb56 | ||
|
|
857715e55c | ||
|
|
5ccc815a47 | ||
|
|
d77c04109b | ||
|
|
1ba8b8b1b1 | ||
|
|
caa349b59b | ||
|
|
9a7350149d | ||
|
|
7f435363f7 | ||
|
|
832cb79f88 | ||
|
|
ba7290fdda | ||
|
|
85ee4a95ec | ||
|
|
eb164c511f | ||
|
|
26e3119ef3 | ||
|
|
6c38b54f7d | ||
|
|
7e6badaf5d | ||
|
|
c104e8e1f0 | ||
|
|
c8d04f3373 | ||
|
|
f64f417711 | ||
|
|
92fe975e3d | ||
|
|
e00731ef8f | ||
|
|
0d7d0664f6 | ||
|
|
b59b0ef041 | ||
|
|
964aa0ecce | ||
|
|
a9ecf06cd4 | ||
|
|
76da6857e7 | ||
|
|
9eef60c93d | ||
|
|
df412bbef7 | ||
|
|
b4c6632162 | ||
|
|
5f2eb6bd32 | ||
|
|
ea532b15fc | ||
|
|
01bd1fde39 | ||
|
|
c6c1c61ff6 | ||
|
|
bbd33c70c1 | ||
|
|
a2592342fe | ||
|
|
db06e12337 | ||
|
|
22139c9650 | ||
|
|
3b1e2e38fb | ||
|
|
6f6da47ebe | ||
|
|
f3237172f3 | ||
|
|
45dc64af5a | ||
|
|
4df71bff7f | ||
|
|
f5003d3505 | ||
|
|
f2f3fab3e2 | ||
|
|
2357e139f0 | ||
|
|
c74eaa47a3 | ||
|
|
591f9fa4c2 | ||
|
|
766ffa3e65 | ||
|
|
78d2c7beaa | ||
|
|
f11c38238a | ||
|
|
6d388d50ee | ||
|
|
9479254562 | ||
|
|
9c9fdaf35d | ||
|
|
a4767591ae | ||
|
|
ecbc50b990 | ||
|
|
dde9cc27d0 | ||
|
|
703c03e84b | ||
|
|
2f831b3e6f | ||
|
|
d54eec0b87 | ||
|
|
bc80554cd3 | ||
|
|
a696d330e5 | ||
|
|
b1479ab3c5 | ||
|
|
e51fb06ceb | ||
|
|
dc0f2bde6b | ||
|
|
eb13f267fe | ||
|
|
8677e73d75 | ||
|
|
fbb919d767 | ||
|
|
1f0f9a2eae | ||
|
|
282b2a0a43 | ||
|
|
6d0e4d5a38 | ||
|
|
43956bbf2c | ||
|
|
18e31b3821 | ||
|
|
0d0acb9d10 | ||
|
|
6a792ce6a0 | ||
|
|
917cea5052 | ||
|
|
c98f0d3c81 | ||
|
|
88d650e638 | ||
|
|
72ebe937fb | ||
|
|
8efa5bc1dc | ||
|
|
9b6d2bce58 | ||
|
|
7bb26a7f07 | ||
|
|
9585b3919a | ||
|
|
f56c9deb00 | ||
|
|
c6221fc60f | ||
|
|
acfe3e912e | ||
|
|
50be0ebe3c | ||
|
|
704dc803b8 | ||
|
|
e2d64e9140 | ||
|
|
bdef0612f7 | ||
|
|
a196b71d36 | ||
|
|
fff8116430 | ||
|
|
2be6fb13d2 | ||
|
|
faa6d7f148 | ||
|
|
982fc2838d | ||
|
|
a8351319f8 | ||
|
|
5b1901708c | ||
|
|
fa8d4f511f | ||
|
|
b4215afe82 | ||
|
|
2ad25e8fbc | ||
|
|
67d0e9d2c2 | ||
|
|
f69aae550f | ||
|
|
2d26acd991 | ||
|
|
8dec1453c4 | ||
|
|
241d1e181f | ||
|
|
f267c597e0 | ||
|
|
192437896d | ||
|
|
d59ae7fce1 | ||
|
|
18b21005f1 | ||
|
|
94ac91cce3 | ||
|
|
46e255bccc | ||
|
|
87de5b5305 | ||
|
|
1ddc923631 | ||
|
|
03f65606fd | ||
|
|
148cf9552b | ||
|
|
bf230322d0 | ||
|
|
758779a8f5 | ||
|
|
55eb39c51d | ||
|
|
13b8475c54 | ||
|
|
d3d52b44ee | ||
|
|
d2886432b7 | ||
|
|
7bb8737ff9 | ||
|
|
3904c36a6e | ||
|
|
c2577df6f7 | ||
|
|
36cb0fb4cd | ||
|
|
ec79793f3d | ||
|
|
188bf76ed1 | ||
|
|
e4e909737c | ||
|
|
d32421c583 | ||
|
|
63cbcdc3ea | ||
|
|
dfd8d0fa3d | ||
|
|
7d7ca11c81 | ||
|
|
ee50a31bba | ||
|
|
179930ad80 | ||
|
|
fdb02da5ed | ||
|
|
5499beb510 | ||
|
|
96046d2ee2 | ||
|
|
a35a209066 | ||
|
|
8139c33b86 | ||
|
|
da40562ee3 | ||
|
|
ce478583ab | ||
|
|
6215af0e5a | ||
|
|
8f0ecac842 | ||
|
|
54dd562d88 | ||
|
|
c24f7a86d4 | ||
|
|
d6b4437664 | ||
|
|
68e74b9830 | ||
|
|
3fa40b2303 | ||
|
|
5feb73c65b | ||
|
|
33d676e1fe | ||
|
|
afb7af287b | ||
|
|
d6291a4c47 | ||
|
|
7228b84bf8 | ||
|
|
2dbf8ebb65 | ||
|
|
2d90844239 | ||
|
|
bf6e66ea47 | ||
|
|
5edaa6ec29 | ||
|
|
881ca67f80 | ||
|
|
1c0ed2a2c1 | ||
|
|
245d27ea71 | ||
|
|
f14d4b8ac3 | ||
|
|
fdc867b9d5 | ||
|
|
8e876d6a99 | ||
|
|
c9de0b4cf2 | ||
|
|
27b3a86155 | ||
|
|
683bdc838d | ||
|
|
4f9f10ddf2 | ||
|
|
6fe3f90a26 | ||
|
|
5bdb7eee85 | ||
|
|
34262a45ca | ||
|
|
7c22110c96 | ||
|
|
6ad9fb8f27 | ||
|
|
4348f4b0d4 | ||
|
|
22529e28d8 | ||
|
|
aa52bcee8c | ||
|
|
d731ab3682 | ||
|
|
7840438bfc | ||
|
|
455a2dba8a | ||
|
|
01c310c913 | ||
|
|
a679d6f2e1 | ||
|
|
e8af56bdbe | ||
|
|
efa099ac5b | ||
|
|
81181aa7e6 | ||
|
|
1ab6640983 | ||
|
|
e5636cde05 | ||
|
|
0a0d00b26e | ||
|
|
6c9afcff81 | ||
|
|
b7933b282b | ||
|
|
653c34758b | ||
|
|
9d18ab795e | ||
|
|
a5c06c67bc | ||
|
|
db2f790bf7 | ||
|
|
5cbc6b5f6a | ||
|
|
f7ecf40633 | ||
|
|
6cd83c60ef | ||
|
|
90ffce63e5 | ||
|
|
2910ebc0fd | ||
|
|
02fab95637 | ||
|
|
22d2209a1b | ||
|
|
e2bea81c64 | ||
|
|
466b8bd1cc | ||
|
|
c5287d2869 | ||
|
|
010502ed9d | ||
|
|
be06e74d94 | ||
|
|
199d2cf851 | ||
|
|
deab1b4962 | ||
|
|
d04411e4a8 | ||
|
|
ce52b57a52 | ||
|
|
c760c19056 | ||
|
|
6b5e469b19 | ||
|
|
8663489ae4 | ||
|
|
877ef1d7ed | ||
|
|
6a81035f10 | ||
|
|
1f7276bbfd | ||
|
|
90e8490a85 | ||
|
|
38d51988d9 | ||
|
|
b8fccd1bac | ||
|
|
ded8e7b916 | ||
|
|
f56ca6318a | ||
|
|
5bd6ba4f36 | ||
|
|
909fb2eb2b | ||
|
|
b2209eaad0 | ||
|
|
dcae3c4acb | ||
|
|
3a3a2ec76d | ||
|
|
5e2ba192c1 | ||
|
|
6d4452bf94 | ||
|
|
998f9491f6 | ||
|
|
fa7efeb283 | ||
|
|
22206b6a99 | ||
|
|
34036ee355 | ||
|
|
2eb2d555ad | ||
|
|
722abf9cab | ||
|
|
357d3b8140 | ||
|
|
01ef79ac33 | ||
|
|
8d2456f238 | ||
|
|
bb164edb55 | ||
|
|
21117eaa17 | ||
|
|
573f823f48 | ||
|
|
47ca3b5d33 | ||
|
|
7334134b45 | ||
|
|
e6be594395 | ||
|
|
e2b6697362 | ||
|
|
f78e11057d | ||
|
|
314ee96525 | ||
|
|
efc1223d45 | ||
|
|
38d9aa4997 | ||
|
|
42c7431416 | ||
|
|
d1e40ee71c | ||
|
|
43169d8bab | ||
|
|
8cbbb52b95 | ||
|
|
807a55fcdc | ||
|
|
8c5351445b | ||
|
|
999c630c5a | ||
|
|
a0186ce8e4 | ||
|
|
9fe336d564 | ||
|
|
34a1d4e6b9 | ||
|
|
bafa548f2f | ||
|
|
fa709a9bdc | ||
|
|
2a1cc7e3ea | ||
|
|
68648b9920 | ||
|
|
b9f297935b | ||
|
|
eb6fc4f8cf | ||
|
|
8a177c34fa | ||
|
|
22a5f411d0 | ||
|
|
f557609e9e | ||
|
|
cde8deaf4b | ||
|
|
48eb1c6d67 | ||
|
|
4db741a87c | ||
|
|
3188ca6884 | ||
|
|
bb9cd171bf | ||
|
|
7a81638258 | ||
|
|
6519db1fe8 | ||
|
|
f313d319d4 | ||
|
|
a8c4e6fedc | ||
|
|
db16458cf5 | ||
|
|
38a9562eaf | ||
|
|
b2327e8bf7 | ||
|
|
aafef107ec | ||
|
|
4b442e75f7 | ||
|
|
e8f103799b | ||
|
|
618685dc05 | ||
|
|
f0d50e53fa | ||
|
|
1b4828f4fa | ||
|
|
56730d67a2 | ||
|
|
b02516b0b3 | ||
|
|
1eb32f86bd | ||
|
|
3b669e64e2 | ||
|
|
fbccd70f7d | ||
|
|
61e5231d99 | ||
|
|
5c101c5a85 | ||
|
|
a6e991923e | ||
|
|
f0c494ecea | ||
|
|
40dfdf9bb1 | ||
|
|
770e0f01f1 | ||
|
|
8448d3e516 | ||
|
|
292a11f942 | ||
|
|
6099986f5b | ||
|
|
53d18d8947 | ||
|
|
f77bdd233b | ||
|
|
dc25a1294c | ||
|
|
8f4bffa950 | ||
|
|
96195fa16a | ||
|
|
4e8a59ad4e | ||
|
|
e42ee6d6be | ||
|
|
f6a04a8b36 | ||
|
|
36b94b81ce | ||
|
|
d66e782152 | ||
|
|
d122d39d3a | ||
|
|
7c3053e8cd | ||
|
|
ecdfa61755 | ||
|
|
7321117c99 | ||
|
|
b50560eadf | ||
|
|
60841e6312 | ||
|
|
66cc61f967 | ||
|
|
1933f4bcc8 | ||
|
|
977f5ca9ce | ||
|
|
e5e529d3d2 | ||
|
|
e1f9d6091c | ||
|
|
a83d8064f2 | ||
|
|
9afb5e7272 | ||
|
|
4c7c2ccc74 | ||
|
|
a6d5686c1c | ||
|
|
8fa1b92deb | ||
|
|
f06e3643b6 | ||
|
|
cd8e59bc9c | ||
|
|
b6eeeea1ea | ||
|
|
aea642fcde | ||
|
|
27bec7ad05 | ||
|
|
80cb3458de | ||
|
|
615048df04 | ||
|
|
4e07b56f3e | ||
|
|
7d18da13a1 | ||
|
|
a49e9e346f | ||
|
|
87fd267183 | ||
|
|
c61fefb55f | ||
|
|
c644031041 | ||
|
|
79f0f52825 | ||
|
|
03ac62044b | ||
|
|
531bc0f887 | ||
|
|
af8b60ca29 | ||
|
|
80c14e9ebd | ||
|
|
3d658eff29 | ||
|
|
8a8a75aa79 | ||
|
|
ea3fe06613 | ||
|
|
92e7f6bd68 | ||
|
|
96b22a00ff | ||
|
|
cfddbc5b3f | ||
|
|
725009d719 | ||
|
|
7f7370f6db | ||
|
|
0fcc1316b2 | ||
|
|
e9933bebe7 | ||
|
|
9810081b11 | ||
|
|
6522d252cb | ||
|
|
a2867cffb7 | ||
|
|
e92c1b53a1 | ||
|
|
52aecb47e7 | ||
|
|
c4652d39fa | ||
|
|
c7b155ccac | ||
|
|
96d18535f8 | ||
|
|
98471e39e1 | ||
|
|
f55fc5776e | ||
|
|
f9a8d1cc40 | ||
|
|
e55a210cf9 | ||
|
|
d19d4406f6 | ||
|
|
9697331abb | ||
|
|
91db02f2da | ||
|
|
b933b30e0a | ||
|
|
2cb9a7c199 | ||
|
|
06ff17ebb2 | ||
|
|
44a573ecd5 | ||
|
|
ffa7952fc1 | ||
|
|
701514a168 | ||
|
|
1565bc9e1a | ||
|
|
0681bad8e6 | ||
|
|
23a21bb39f | ||
|
|
9aabb575c1 | ||
|
|
cf607e892b | ||
|
|
6bf906f2a7 | ||
|
|
7e897d604c | ||
|
|
39887df3ec | ||
|
|
fbb7a684bf | ||
|
|
449ad6cd0d | ||
|
|
cfcd6e5f93 | ||
|
|
73aab88109 | ||
|
|
beb692a3c4 | ||
|
|
fe8385e759 | ||
|
|
58fb331019 | ||
|
|
e4986a6a47 | ||
|
|
fcd47ecec6 | ||
|
|
174e6ad637 | ||
|
|
2ac4097e32 | ||
|
|
dd13ac5cbb | ||
|
|
9c55a898f7 | ||
|
|
bb9018c094 | ||
|
|
b09b4183bb | ||
|
|
3d7bc6583d | ||
|
|
99e8be227e | ||
|
|
b1c38cc793 | ||
|
|
26e4e70fc8 | ||
|
|
04bdf3f26a | ||
|
|
cafeacf69f | ||
|
|
4c2d6d78ea | ||
|
|
ea7d0cdef5 | ||
|
|
47bae63548 | ||
|
|
8470f4f7fb | ||
|
|
242f9d55ed | ||
|
|
236ac52925 | ||
|
|
baab9a9c89 | ||
|
|
3d26e6e340 | ||
|
|
c34ab48972 | ||
|
|
cf0abf21db | ||
|
|
7918b392e8 | ||
|
|
f0ced15e6a | ||
|
|
c0204cb725 | ||
|
|
eb3905447f | ||
|
|
16c7459906 | ||
|
|
eae5f87eed | ||
|
|
d4b85e3b1c | ||
|
|
a989c3b7d0 | ||
|
|
d96ba9fa46 | ||
|
|
2261d655ea | ||
|
|
8b67049863 | ||
|
|
48258dd9dd | ||
|
|
e04c573726 | ||
|
|
4daec3794e | ||
|
|
f0646dde32 | ||
|
|
ec2086e0f7 | ||
|
|
2ac704178c | ||
|
|
7c43c38e28 | ||
|
|
043a8336e5 | ||
|
|
ee27535b82 | ||
|
|
89e6ae2475 | ||
|
|
e51615f520 | ||
|
|
6e59897587 | ||
|
|
bd0644a261 | ||
|
|
d7825e1bf8 | ||
|
|
97956b6c5e | ||
|
|
8150504ea0 | ||
|
|
942dec81c7 | ||
|
|
dcbc377c62 | ||
|
|
7b65233699 | ||
|
|
eabfd8b5c4 | ||
|
|
b7b2799611 | ||
|
|
ece59ac374 | ||
|
|
83cc8b772c | ||
|
|
8b5bc18142 | ||
|
|
e1413baa9a | ||
|
|
03640e6d37 | ||
|
|
2588011524 | ||
|
|
0c16a6804a | ||
|
|
e2021fdf5d | ||
|
|
33b5694d35 | ||
|
|
af7d04fef6 | ||
|
|
14e3419e4c | ||
|
|
f6c5252376 | ||
|
|
78ee77444f | ||
|
|
fb022970b5 | ||
|
|
e5d378dc93 | ||
|
|
5969902263 | ||
|
|
d5894de142 | ||
|
|
059c6baa4e | ||
|
|
bbf9331baf | ||
|
|
a5b084f506 | ||
|
|
3b5471b97c | ||
|
|
fae37fe67d | ||
|
|
1f975efda1 | ||
|
|
3b3b61f52c | ||
|
|
1c1f6e21c5 | ||
|
|
8b60c7cc09 | ||
|
|
d4f9752cd4 | ||
|
|
6332a37f5b | ||
|
|
840a583a0b | ||
|
|
e7ed80d3e0 | ||
|
|
c2b9376313 | ||
|
|
7a8b291189 | ||
|
|
e63f71d3a7 | ||
|
|
061c97caca | ||
|
|
988d28f2fd | ||
|
|
af8a0ea9c0 | ||
|
|
c78e8d8358 | ||
|
|
392f7098bc | ||
|
|
195f918543 | ||
|
|
b2579538fa | ||
|
|
35b97c4f45 | ||
|
|
adaa89a963 | ||
|
|
fc1429fae5 | ||
|
|
4090f95098 | ||
|
|
3189d8d43e | ||
|
|
cd6d10e428 | ||
|
|
7d37236d78 | ||
|
|
7c127acf17 | ||
|
|
0a4ce2350a | ||
|
|
181a70cb1f | ||
|
|
7438545a16 | ||
|
|
9e902a5dd4 | ||
|
|
91e395884f | ||
|
|
7107c68a41 | ||
|
|
86728b79e3 | ||
|
|
e04f901590 | ||
|
|
47ad2b5cfa | ||
|
|
cea0f1766f | ||
|
|
407e57d334 | ||
|
|
c4e66d0c9c | ||
|
|
c54f1ca9b0 | ||
|
|
f9fdc36d0d | ||
|
|
6d546094ef | ||
|
|
8dc5ae4f86 | ||
|
|
fd7b81af10 | ||
|
|
3abd859664 | ||
|
|
6134ce39d4 | ||
|
|
8ea42ea432 | ||
|
|
fd4ff00ee2 | ||
|
|
9d981b09ca | ||
|
|
ed807e0fe5 | ||
|
|
32b962b036 | ||
|
|
8ac476b6bd | ||
|
|
9ab8b84d81 | ||
|
|
05cc4b4bd1 | ||
|
|
b28cf30f47 | ||
|
|
5bf8b0a2b6 | ||
|
|
d2891a49fc | ||
|
|
bec8ba6c2f | ||
|
|
9e097be657 | ||
|
|
b008f55847 | ||
|
|
487d90f260 | ||
|
|
671a745acc | ||
|
|
80880f4d2a | ||
|
|
109fba828b | ||
|
|
0a845fdfa6 | ||
|
|
5a5b8464fc | ||
|
|
86d0b92a66 | ||
|
|
9f75234dac | ||
|
|
a58b129503 | ||
|
|
6d752acb28 | ||
|
|
4486582006 | ||
|
|
ed027fd3ce | ||
|
|
a92cb848c0 | ||
|
|
e573e36aa6 | ||
|
|
fc61753953 | ||
|
|
9b74e19a99 | ||
|
|
e9e39cb985 | ||
|
|
d938f2ea50 | ||
|
|
6c3535951f | ||
|
|
cee78b4ae3 | ||
|
|
1ee646e1ce | ||
|
|
6199dec82f | ||
|
|
59f307bdb3 | ||
|
|
24747355ce | ||
|
|
3d287a2827 | ||
|
|
def28df4dd | ||
|
|
d90e698a15 | ||
|
|
f4d6f88e5c | ||
|
|
799dce4b13 | ||
|
|
a025d028d4 | ||
|
|
8cfa9a891d | ||
|
|
5b44439dd2 | ||
|
|
44fb7dcdaa | ||
|
|
4f5d1ba26b | ||
|
|
0a08b09d70 | ||
|
|
35eedda352 | ||
|
|
05fb8bc742 | ||
|
|
795df2937e | ||
|
|
5607740e77 | ||
|
|
d4287d3046 | ||
|
|
71ef72bae3 | ||
|
|
c310d698ca | ||
|
|
13ce3d1c92 | ||
|
|
122ed79a98 | ||
|
|
6d655242a6 | ||
|
|
09cab9d825 | ||
|
|
d8bd38c124 | ||
|
|
abc8bf0e62 | ||
|
|
24106ac3d2 | ||
|
|
bf506d382f | ||
|
|
84b1a9bbec | ||
|
|
d67afd4396 | ||
|
|
9f2f7eff5b | ||
|
|
c863d5976b | ||
|
|
d6338e9daf | ||
|
|
b393d2666d | ||
|
|
c5b1e73312 | ||
|
|
fa65222228 | ||
|
|
38e57ccd71 | ||
|
|
86d3d26273 | ||
|
|
db8e4a03a8 | ||
|
|
c90db8594a | ||
|
|
855ca77194 | ||
|
|
4f23af070e | ||
|
|
626613b545 | ||
|
|
c26de3bd67 | ||
|
|
c059915bd3 | ||
|
|
d2d70dcb38 | ||
|
|
846292442c | ||
|
|
11283a2199 | ||
|
|
4c8a0b9a63 | ||
|
|
54accbf21f | ||
|
|
08dc0db2e2 | ||
|
|
38c6330a2a | ||
|
|
2f983f42fe | ||
|
|
b9e45c4bb0 | ||
|
|
9d3b1d75d1 | ||
|
|
ac78ddcc82 | ||
|
|
f912d84ab6 | ||
|
|
85548d6e8e | ||
|
|
c74de12ceb | ||
|
|
017a21c17f | ||
|
|
9a63038a5f | ||
|
|
304fc2536b | ||
|
|
365169a9fb | ||
|
|
4b82ead18b | ||
|
|
141d7cc268 | ||
|
|
ee8e94cfb0 | ||
|
|
b50bedef39 | ||
|
|
a689ce4de9 | ||
|
|
ae0478a7e9 | ||
|
|
ee6eed5ac5 | ||
|
|
9140c426c4 | ||
|
|
a41bfabede | ||
|
|
5d26357025 | ||
|
|
644c10e994 | ||
|
|
b3ca0beead | ||
|
|
93300c6821 | ||
|
|
2bd8d7ee32 | ||
|
|
95c339ef74 | ||
|
|
76ad1d5ef7 | ||
|
|
151e82a2c3 | ||
|
|
3fcaf93788 | ||
|
|
b8927d66e9 | ||
|
|
9273f364fc | ||
|
|
bc42dbf88a | ||
|
|
481d4c15d6 | ||
|
|
563030e732 | ||
|
|
4f9137d009 | ||
|
|
ed1b4d1885 | ||
|
|
6bbcd9020e | ||
|
|
0ba916a7d6 | ||
|
|
cf498462eb | ||
|
|
e8b8b861a4 | ||
|
|
4afb045234 | ||
|
|
ef5c876085 | ||
|
|
7fbcfeec9c | ||
|
|
2981a8ef83 | ||
|
|
b1e8bfe2ab | ||
|
|
623d568eb9 | ||
|
|
3625834be3 | ||
|
|
925a4f4a57 |
195
.github/workflows/main.yml
vendored
Normal file
195
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
# Workflow for testing MULTI-Module firmware builds
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
# Trigger the workflow on pushes, except those that are tagged (avoids double-testing releases)
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'buildroot/bin/**'
|
||||
- 'Multiprotocol/**'
|
||||
|
||||
# Trigger the workflow on pull requests to the master branch
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/**'
|
||||
- 'buildroot/bin/**'
|
||||
- 'Multiprotocol/**'
|
||||
|
||||
# Triggers the workflow on release creation
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
# Allows the workflow to be triggered manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Configure the board matrix
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board: [
|
||||
"multi4in1:avr:multiatmega328p:bootloader=none",
|
||||
"multi4in1:avr:multiatmega328p:bootloader=optiboot",
|
||||
"multi4in1:avr:multixmega32d4",
|
||||
"multi4in1:STM32F1:multi5in1t18int",
|
||||
"multi4in1:STM32F1:multistm32f103cb:debug_option=none",
|
||||
"multi4in1:STM32F1:multistm32f103cb:debug_option=native",
|
||||
"multi4in1:STM32F1:multistm32f103cb:debug_option=ftdi",
|
||||
"multi4in1:STM32F1:multistm32f103c8:debug_option=none"
|
||||
]
|
||||
|
||||
# Set the environment variables
|
||||
env:
|
||||
BOARD: ${{ matrix.board }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Arduino CLI
|
||||
uses: arduino/setup-arduino-cli@v1.1.1
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo "Github Ref: $GITHUB_REF"
|
||||
echo "Event name: ${{ github.event_name }}"
|
||||
echo "Event action: ${{ github.event.action }}"
|
||||
echo "Tag name: ${{ github.event.release.tag_name }}"
|
||||
|
||||
arduino-cli config init --additional-urls https://raw.githubusercontent.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/master/package_multi_4in1_board_index.json
|
||||
arduino-cli core update-index
|
||||
|
||||
if [[ "$BOARD" =~ "multi4in1:avr:" ]]; then
|
||||
arduino-cli core install arduino:avr;
|
||||
arduino-cli core install multi4in1:avr
|
||||
fi
|
||||
|
||||
if [[ "$BOARD" =~ "multi4in1:STM32F1:" ]]; then
|
||||
arduino-cli core install multi4in1:STM32F1
|
||||
fi
|
||||
|
||||
chmod +x ${GITHUB_WORKSPACE}/buildroot/bin/*
|
||||
echo "${GITHUB_WORKSPACE}/buildroot/bin" >> $GITHUB_PATH
|
||||
|
||||
mkdir ./build
|
||||
mkdir ./binaries
|
||||
|
||||
- name: Configure MULTI-Module firmware options
|
||||
run: |
|
||||
# Load the build functions
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
|
||||
# Get the version
|
||||
getMultiVersion
|
||||
echo "MULTI_VERSION=$(echo $MULTI_VERSION)" >> $GITHUB_ENV
|
||||
|
||||
# Get all the protocols for this board
|
||||
getAllProtocols
|
||||
echo "A7105_PROTOCOLS=$(echo $A7105_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "CC2500_PROTOCOLS=$(echo $CC2500_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "CYRF6936_PROTOCOLS=$(echo $CYRF6936_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "NRF24L01_PROTOCOLS=$(echo $NRF24L01_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "SX1276_PROTOCOLS=$(echo $SX1276_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "CCNRF_INO_PROTOCOLS=$(echo $CCNRF_INO_PROTOCOLS)" >> $GITHUB_ENV
|
||||
echo "ALL_PROTOCOLS=$(echo $ALL_PROTOCOLS)" >> $GITHUB_ENV
|
||||
|
||||
# Get all the RF modules for this board
|
||||
getAllRFModules
|
||||
echo "ALL_RFMODULES=$(echo $ALL_RFMODULES)" >> $GITHUB_ENV
|
||||
|
||||
# Disable CHECK_FOR_BOOTLOADER when not needed
|
||||
if [[ "$BOARD" == "multi4in1:avr:multiatmega328p:bootloader=none" ]]; then
|
||||
opt_disable CHECK_FOR_BOOTLOADER;
|
||||
fi
|
||||
|
||||
# Trim the build down for the Atmega328p board
|
||||
if [[ "$BOARD" =~ "multi4in1:avr:multiatmega328p:" ]]; then
|
||||
opt_disable $ALL_PROTOCOLS
|
||||
opt_enable FRSKYX_CC2500_INO AFHDS2A_A7105_INO MJXQ_NRF24L01_INO DSM_CYRF6936_INO;
|
||||
fi
|
||||
|
||||
# Trim the enabled protocols down for the STM32F103CB board with debugging or the STM32F103C8 board in general
|
||||
if [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=ftdi" ]] || [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=native" ]] || [[ "$BOARD" =~ "multi4in1:STM32F1:multistm32f103c8" ]]; then
|
||||
opt_disable $ALL_PROTOCOLS;
|
||||
opt_enable FRSKYX_CC2500_INO AFHDS2A_A7105_INO MJXQ_NRF24L01_INO DSM_CYRF6936_INO;
|
||||
fi
|
||||
|
||||
- name: Save default firmware configuration
|
||||
run: |
|
||||
cat Multiprotocol/_Config.h
|
||||
cp Multiprotocol/_Config.h ./_Config.h.bak
|
||||
|
||||
- name: Build default configuration
|
||||
run: |
|
||||
# Skip the default build for boards where it's too large now
|
||||
if [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=none" ]] || [[ "$BOARD" == "multi4in1:STM32F1:multi5in1t18int" ]]; then
|
||||
printf "Not testing default build for $BOARD.";
|
||||
else
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
buildMulti
|
||||
fi
|
||||
|
||||
- name: Build serial only
|
||||
run: |
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
cp ./_Config.h.bak Multiprotocol/_Config.h
|
||||
opt_disable ENABLE_PPM;
|
||||
buildMulti;
|
||||
|
||||
- name: Build PPM only
|
||||
run: |
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
cp ./_Config.h.bak Multiprotocol/_Config.h
|
||||
opt_disable ENABLE_SERIAL;
|
||||
buildMulti;
|
||||
|
||||
- name: Build each RF module individually
|
||||
run: |
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
cp ./_Config.h.bak Multiprotocol/_Config.h;
|
||||
buildEachRFModule;
|
||||
|
||||
- name: Build each protocol individually
|
||||
run: |
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
cp ./_Config.h.bak Multiprotocol/_Config.h;
|
||||
buildEachProtocol;
|
||||
|
||||
- name: Build release files
|
||||
run: |
|
||||
source ./buildroot/bin/buildFunctions;
|
||||
cp ./_Config.h.bak Multiprotocol/_Config.h;
|
||||
buildReleaseFiles;
|
||||
ls -al ./binaries;
|
||||
|
||||
NUM_FILES=$(ls -l ./binaries | grep ^- | wc -l);
|
||||
if [ $NUM_FILES -gt 0 ]; then
|
||||
echo "HAVE_FILES=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "HAVE_FILES=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Deploy files to release
|
||||
if: github.event_name == 'release' && github.event.action == 'created' && env.HAVE_FILES == 'true'
|
||||
uses: AButler/upload-release-assets@v2.0
|
||||
with:
|
||||
files: './binaries/*'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 'Upload Artifacts'
|
||||
if: env.HAVE_FILES == 'true'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: multi-test-build
|
||||
path: ./binaries/
|
||||
2
BootLoaders/AtmegaEmptyBoot/AtmegaMultiEmpty.hex
Normal file
2
BootLoaders/AtmegaEmptyBoot/AtmegaMultiEmpty.hex
Normal file
@@ -0,0 +1,2 @@
|
||||
:02000000FFFF00
|
||||
:00000001FF
|
||||
34
BootLoaders/AtmegaMultiBoot/AtmegaMultiBoot.hex
Normal file
34
BootLoaders/AtmegaMultiBoot/AtmegaMultiBoot.hex
Normal file
@@ -0,0 +1,34 @@
|
||||
:107E0000112484B714BE9FEF9BB99CE395B991E010
|
||||
:107E100098B98370A9F08AEF80938500109284004E
|
||||
:107E200085E08093810096BBB09BFECF10928100CD
|
||||
:107E300093B186B181709C73892B8D3109F0B3D0D9
|
||||
:107E400082E08093C00088E18093C10086E0809347
|
||||
:107E5000C20081E28093C400259AC0E0D0E093E0A4
|
||||
:107E6000F92EEE24E39425E0D22E31E1C32EA9D0E1
|
||||
:107E7000813481F4A6D08EBBABD08EB3823811F49E
|
||||
:107E800085E006C08EB3813811F484E001C083E040
|
||||
:107E900091D086C0823411F484E103C0853419F492
|
||||
:107EA00085E09DD07DC0853541F48BD0C82F89D029
|
||||
:107EB000D0E0D82BCC0FDD1F72C0863521F484E0D2
|
||||
:107EC0008ED080E0E5CF843609F03DC07AD079D0FD
|
||||
:107ED000B82E77D0C11520E7D20718F000E011E0E6
|
||||
:107EE00004C0FE01F7BEE895F9CF6BD0F80181938D
|
||||
:107EF0008F01BE12FACFCE01905781159E4018F423
|
||||
:107F0000FE01F7BEE89564D0C115FEE7DF0708F073
|
||||
:107F100047C007B600FCFDCFFE01A0E0B1E08D91A7
|
||||
:107F20009D910C01E7BEE89511243296A03821E01E
|
||||
:107F3000B207A9F7FE01D7BEE89507B600FCFDCF52
|
||||
:107F4000C7BEE8952DC08437B1F43BD03AD0B82EE7
|
||||
:107F500038D03ED0FE01AC2EAB0C8F010F5F1F4F0F
|
||||
:107F6000849128D0A01205C02196BA94CB0DD11DC2
|
||||
:107F700017C0F801F2CF853739F42AD08EE11AD034
|
||||
:107F800085E918D08FE084CF813549F421D080E194
|
||||
:107F900011D08091C00086FFFCCF05D001C018D061
|
||||
:107FA00080E108D064CFE0E0F0E084918F3F09F0F9
|
||||
:107FB000099408959091C00095FFFCCF8093C6006E
|
||||
:107FC00008958091C00087FFFCCF8091C60008957E
|
||||
:107FD000F8DF803211F085E1EDDF84E1EBCFCF9364
|
||||
:107FE000C82FEFDFC150E9F7CF91F2CFA8950895E0
|
||||
:0C7FF000E0E6F0E098E1908380830895C3
|
||||
:0400000300007E007B
|
||||
:00000001FF
|
||||
504
BootLoaders/AtmegaMultiBoot/Source/Makefile
Normal file
504
BootLoaders/AtmegaMultiBoot/Source/Makefile
Normal file
@@ -0,0 +1,504 @@
|
||||
# Makefile for ATmegaBOOT
|
||||
# E.Lins, 18.7.2005
|
||||
# $Id$
|
||||
#
|
||||
# Instructions
|
||||
#
|
||||
# To make bootloader .hex file:
|
||||
# make diecimila
|
||||
# make lilypad
|
||||
# make ng
|
||||
# etc...
|
||||
#
|
||||
# To burn bootloader .hex file:
|
||||
# make diecimila_isp
|
||||
# make lilypad_isp
|
||||
# make ng_isp
|
||||
# etc...
|
||||
|
||||
# program name should not be changed...
|
||||
PROGRAM = optiboot
|
||||
|
||||
# The default behavior is to build using tools that are in the users
|
||||
# current path variables, but we can also build using an installed
|
||||
# Arduino user IDE setup, or the Arduino source tree.
|
||||
# Uncomment this next lines to build within the arduino environment,
|
||||
# using the arduino-included avrgcc toolset (mac and pc)
|
||||
# ENV ?= arduino
|
||||
# ENV ?= arduinodev
|
||||
# OS ?= macosx
|
||||
# OS ?= windows
|
||||
|
||||
|
||||
# enter the parameters for the avrdude isp tool -b19200
|
||||
#
|
||||
# These are the parameters for a usb-based STK500v2 programmer.
|
||||
# Exact type unknown. (historical Makefile values.)
|
||||
ISPTOOL = stk500v2
|
||||
ISPPORT = usb
|
||||
ISPSPEED = -b 57600
|
||||
#
|
||||
#
|
||||
# These are parameters for using an Arduino with the ArduinoISP sketch
|
||||
# as the programmer. On a mac, for a particular Uno as programmer.
|
||||
#ISPTOOL = stk500v1 -C /Applications/arduino/arduino-0022/hardware/tools/avr/etc/avrdude.conf
|
||||
#ISPPORT = /dev/tty.usbmodemfd3141
|
||||
#ISPSPEED = -b19200
|
||||
|
||||
MCU_TARGET = atmega168
|
||||
LDSECTIONS = -Wl,--section-start=.text=0x3e00 -Wl,--section-start=.version=0x3ffe
|
||||
|
||||
# Build environments
|
||||
# Start of some ugly makefile-isms to allow optiboot to be built
|
||||
# in several different environments. See the README.TXT file for
|
||||
# details.
|
||||
|
||||
# default
|
||||
fixpath = $(1)
|
||||
|
||||
ifeq ($(ENV), arduino)
|
||||
# For Arduino, we assume that we're connected to the optiboot directory
|
||||
# included with the arduino distribution, which means that the full set
|
||||
# of avr-tools are "right up there" in standard places.
|
||||
TOOLROOT = ../../../tools
|
||||
GCCROOT = $(TOOLROOT)/avr/bin/
|
||||
AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf
|
||||
|
||||
ifeq ($(OS), windows)
|
||||
# On windows, SOME of the tool paths will need to have backslashes instead
|
||||
# of forward slashes (because they use windows cmd.exe for execution instead
|
||||
# of a unix/mingw shell?) We also have to ensure that a consistent shell
|
||||
# is used even if a unix shell is installed (ie as part of WINAVR)
|
||||
fixpath = $(subst /,\,$1)
|
||||
SHELL = cmd.exe
|
||||
endif
|
||||
|
||||
else ifeq ($(ENV), arduinodev)
|
||||
# Arduino IDE source code environment. Use the unpacked compilers created
|
||||
# by the build (you'll need to do "ant build" first.)
|
||||
ifeq ($(OS), macosx)
|
||||
TOOLROOT = ../../../../build/macosx/work/Arduino.app/Contents/Resources/Java/hardware/tools
|
||||
endif
|
||||
ifeq ($(OS), windows)
|
||||
TOOLROOT = ../../../../build/windows/work/hardware/tools
|
||||
endif
|
||||
|
||||
GCCROOT = $(TOOLROOT)/avr/bin/
|
||||
AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf
|
||||
|
||||
else
|
||||
GCCROOT =
|
||||
AVRDUDE_CONF =
|
||||
endif
|
||||
#
|
||||
# End of build environment code.
|
||||
|
||||
|
||||
# the efuse should really be 0xf8; since, however, only the lower
|
||||
# three bits of that byte are used on the atmega168, avrdude gets
|
||||
# confused if you specify 1's for the higher bits, see:
|
||||
# http://tinker.it/now/2007/02/24/the-tale-of-avrdude-atmega168-and-extended-bits-fuses/
|
||||
#
|
||||
# similarly, the lock bits should be 0xff instead of 0x3f (to
|
||||
# unlock the bootloader section) and 0xcf instead of 0x2f (to
|
||||
# lock it), but since the high two bits of the lock byte are
|
||||
# unused, avrdude would get confused.
|
||||
|
||||
ISPFUSES = $(GCCROOT)avrdude $(AVRDUDE_CONF) -c $(ISPTOOL) \
|
||||
-p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \
|
||||
-e -u -U lock:w:0x3f:m -U efuse:w:0x$(EFUSE):m \
|
||||
-U hfuse:w:0x$(HFUSE):m -U lfuse:w:0x$(LFUSE):m
|
||||
ISPFLASH = $(GCCROOT)avrdude $(AVRDUDE_CONF) -c $(ISPTOOL) \
|
||||
-p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \
|
||||
-U flash:w:$(PROGRAM)_$(TARGET).hex -U lock:w:0x2f:m
|
||||
|
||||
STK500 = "C:\Program Files\Atmel\AVR Tools\STK500\Stk500.exe"
|
||||
STK500-1 = $(STK500) -e -d$(MCU_TARGET) -pf -vf -if$(PROGRAM)_$(TARGET).hex \
|
||||
-lFF -LFF -f$(HFUSE)$(LFUSE) -EF8 -ms -q -cUSB -I200kHz -s -wt
|
||||
STK500-2 = $(STK500) -d$(MCU_TARGET) -ms -q -lCF -LCF -cUSB -I200kHz -s -wt
|
||||
|
||||
OBJ = $(PROGRAM).o
|
||||
OPTIMIZE = -Os -fno-inline-small-functions -fno-split-wide-types
|
||||
# -mshort-calls
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
CC = $(GCCROOT)avr-gcc
|
||||
|
||||
# Override is only needed by avr-lib build system.
|
||||
|
||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) -DF_CPU=$(AVR_FREQ) $(DEFS)
|
||||
override LDFLAGS = $(LDSECTIONS) -Wl,--relax -Wl,--gc-sections -nostartfiles -nostdlib
|
||||
|
||||
OBJCOPY = $(GCCROOT)avr-objcopy
|
||||
OBJDUMP = $(call fixpath,$(GCCROOT)avr-objdump)
|
||||
|
||||
SIZE = $(GCCROOT)avr-size
|
||||
|
||||
|
||||
|
||||
#Voice board test
|
||||
# ATmega328
|
||||
#
|
||||
#atmega328: TARGET = atmega328p
|
||||
#atmega328: MCU_TARGET = atmega328p
|
||||
#atmega328: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
#atmega328: AVR_FREQ = 12000000L
|
||||
#atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
#atmega328: $(PROGRAM)_atmega328.hex
|
||||
#atmega328: $(PROGRAM)_atmega328.lst
|
||||
|
||||
atmega328: TARGET = atmega328
|
||||
atmega328: MCU_TARGET = atmega328p
|
||||
atmega328: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=57600'
|
||||
atmega328: AVR_FREQ = 16000000L
|
||||
atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
atmega328: $(PROGRAM)_atmega328_16.hex
|
||||
atmega328: $(PROGRAM)_atmega328_16.lst
|
||||
|
||||
|
||||
xmega32D4: TARGET = atxmega32d4
|
||||
xmega32D4: MCU_TARGET = atxmega32d4
|
||||
xmega32D4: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=57600'
|
||||
xmega32D4: AVR_FREQ = 32000000L
|
||||
xmega32D4: LDSECTIONS = -Wl,--section-start=.text=0x8000
|
||||
xmega32D4: $(PROGRAM)_xmega32d4.hex
|
||||
xmega32D4: $(PROGRAM)_xmega32d4.lst
|
||||
|
||||
|
||||
# Test platforms
|
||||
# Virtual boot block test
|
||||
virboot328: TARGET = atmega328
|
||||
virboot328: MCU_TARGET = atmega328p
|
||||
virboot328: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DVIRTUAL_BOOT'
|
||||
virboot328: AVR_FREQ = 16000000L
|
||||
virboot328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
virboot328: $(PROGRAM)_atmega328.hex
|
||||
virboot328: $(PROGRAM)_atmega328.lst
|
||||
|
||||
# 20MHz clocked platforms
|
||||
#
|
||||
# These are capable of 230400 baud, or 38400 baud on PC (Arduino Avrdude issue)
|
||||
#
|
||||
|
||||
pro20: TARGET = pro_20mhz
|
||||
pro20: MCU_TARGET = atmega168
|
||||
pro20: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro20: AVR_FREQ = 20000000L
|
||||
pro20: $(PROGRAM)_pro_20mhz.hex
|
||||
pro20: $(PROGRAM)_pro_20mhz.lst
|
||||
|
||||
pro20_isp: pro20
|
||||
pro20_isp: TARGET = pro_20mhz
|
||||
# 2.7V brownout
|
||||
pro20_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro20_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro20_isp: EFUSE = 04
|
||||
pro20_isp: isp
|
||||
|
||||
# 16MHz clocked platforms
|
||||
#
|
||||
# These are capable of 230400 baud, or 38400 baud on PC (Arduino Avrdude issue)
|
||||
#
|
||||
|
||||
pro16: TARGET = pro_16MHz
|
||||
pro16: MCU_TARGET = atmega168
|
||||
pro16: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro16: AVR_FREQ = 16000000L
|
||||
pro16: $(PROGRAM)_pro_16MHz.hex
|
||||
pro16: $(PROGRAM)_pro_16MHz.lst
|
||||
|
||||
pro16_isp: pro16
|
||||
pro16_isp: TARGET = pro_16MHz
|
||||
# 2.7V brownout
|
||||
pro16_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro16_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro16_isp: EFUSE = 04
|
||||
pro16_isp: isp
|
||||
|
||||
# Diecimila, Duemilanove with m168, and NG use identical bootloaders
|
||||
# Call it "atmega168" for generality and clarity, keep "diecimila" for
|
||||
# backward compatibility of makefile
|
||||
#
|
||||
atmega168: TARGET = atmega168
|
||||
atmega168: MCU_TARGET = atmega168
|
||||
atmega168: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
atmega168: AVR_FREQ = 12000000L
|
||||
atmega168: $(PROGRAM)_atmega168.hex
|
||||
atmega168: $(PROGRAM)_atmega168.lst
|
||||
|
||||
atmega168_isp: atmega168
|
||||
atmega168_isp: TARGET = atmega168
|
||||
# 2.7V brownout
|
||||
atmega168_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega168_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
atmega168_isp: EFUSE = 04
|
||||
atmega168_isp: isp
|
||||
|
||||
diecimila: TARGET = diecimila
|
||||
diecimila: MCU_TARGET = atmega168
|
||||
diecimila: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
diecimila: AVR_FREQ = 16000000L
|
||||
diecimila: $(PROGRAM)_diecimila.hex
|
||||
diecimila: $(PROGRAM)_diecimila.lst
|
||||
|
||||
diecimila_isp: diecimila
|
||||
diecimila_isp: TARGET = diecimila
|
||||
# 2.7V brownout
|
||||
diecimila_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
diecimila_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
diecimila_isp: EFUSE = 04
|
||||
diecimila_isp: isp
|
||||
|
||||
atmega328_isp: atmega328
|
||||
atmega328_isp: TARGET = atmega328
|
||||
atmega328_isp: MCU_TARGET = atmega328p
|
||||
# 512 byte boot, SPIEN
|
||||
atmega328_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega328_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega328_isp: EFUSE = FD
|
||||
atmega328_isp: isp
|
||||
|
||||
atmega1284: TARGET = atmega1284p
|
||||
atmega1284: MCU_TARGET = atmega1284p
|
||||
atmega1284: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
atmega1284: AVR_FREQ = 16000000L
|
||||
atmega1284: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe
|
||||
atmega1284: $(PROGRAM)_atmega1284p.hex
|
||||
atmega1284: $(PROGRAM)_atmega1284p.lst
|
||||
|
||||
atmega1284_isp: atmega1284
|
||||
atmega1284_isp: TARGET = atmega1284p
|
||||
atmega1284_isp: MCU_TARGET = atmega1284p
|
||||
# 1024 byte boot
|
||||
atmega1284_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega1284_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega1284_isp: EFUSE = FD
|
||||
atmega1284_isp: isp
|
||||
|
||||
# Sanguino has a minimum boot size of 1024 bytes, so enable extra functions
|
||||
#
|
||||
sanguino: TARGET = atmega644p
|
||||
sanguino: MCU_TARGET = atmega644p
|
||||
sanguino: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
sanguino: AVR_FREQ = 16000000L
|
||||
sanguino: LDSECTIONS = -Wl,--section-start=.text=0xfc00 -Wl,--section-start=.version=0xfffe
|
||||
sanguino: $(PROGRAM)_atmega644p.hex
|
||||
sanguino: $(PROGRAM)_atmega644p.lst
|
||||
|
||||
sanguino_isp: sanguino
|
||||
sanguino_isp: TARGET = atmega644p
|
||||
sanguino_isp: MCU_TARGET = atmega644p
|
||||
# 1024 byte boot
|
||||
sanguino_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
sanguino_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
sanguino_isp: EFUSE = FD
|
||||
sanguino_isp: isp
|
||||
|
||||
# Mega has a minimum boot size of 1024 bytes, so enable extra functions
|
||||
#mega: TARGET = atmega1280
|
||||
mega1280: MCU_TARGET = atmega1280
|
||||
mega1280: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
mega1280: AVR_FREQ = 16000000L
|
||||
mega1280: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe
|
||||
mega1280: $(PROGRAM)_atmega1280.hex
|
||||
mega1280: $(PROGRAM)_atmega1280.lst
|
||||
|
||||
mega1280_isp: mega
|
||||
mega1280_isp: TARGET = atmega1280
|
||||
mega1280_isp: MCU_TARGET = atmega1280
|
||||
# 1024 byte boot
|
||||
mega1280_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
mega1280_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
mega1280_isp: EFUSE = FD
|
||||
mega1280_isp: isp
|
||||
|
||||
# ATmega8
|
||||
#
|
||||
atmega8: TARGET = atmega8
|
||||
atmega8: MCU_TARGET = atmega8
|
||||
atmega8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
atmega8: AVR_FREQ = 16000000L
|
||||
atmega8: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe
|
||||
atmega8: $(PROGRAM)_atmega8.hex
|
||||
atmega8: $(PROGRAM)_atmega8.lst
|
||||
|
||||
atmega8_isp: atmega8
|
||||
atmega8_isp: TARGET = atmega8
|
||||
atmega8_isp: MCU_TARGET = atmega8
|
||||
# SPIEN, CKOPT, Bootsize=512B
|
||||
atmega8_isp: HFUSE = CC
|
||||
# 2.7V brownout, Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega8_isp: LFUSE = BF
|
||||
atmega8_isp: isp
|
||||
|
||||
# ATmega88
|
||||
#
|
||||
atmega88: TARGET = atmega88
|
||||
atmega88: MCU_TARGET = atmega88
|
||||
atmega88: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
atmega88: AVR_FREQ = 12000000L
|
||||
atmega88: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe
|
||||
atmega88: $(PROGRAM)_atmega88.hex
|
||||
atmega88: $(PROGRAM)_atmega88.lst
|
||||
|
||||
atmega88_isp: atmega88
|
||||
atmega88_isp: TARGET = atmega88
|
||||
atmega88_isp: MCU_TARGET = atmega88
|
||||
# 2.7V brownout
|
||||
atmega88_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atemga88_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
atmega88_isp: EFUSE = 04
|
||||
atmega88_isp: isp
|
||||
|
||||
|
||||
# 8MHz clocked platforms
|
||||
#
|
||||
# These are capable of 38400 baud
|
||||
#
|
||||
|
||||
lilypad: TARGET = lilypad
|
||||
lilypad: MCU_TARGET = atmega168
|
||||
lilypad: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
lilypad: AVR_FREQ = 8000000L
|
||||
lilypad: $(PROGRAM)_lilypad.hex
|
||||
lilypad: $(PROGRAM)_lilypad.lst
|
||||
|
||||
lilypad_isp: lilypad
|
||||
lilypad_isp: TARGET = lilypad
|
||||
# 2.7V brownout
|
||||
lilypad_isp: HFUSE = DD
|
||||
# Internal 8MHz osc (8MHz) Slow rising power
|
||||
lilypad_isp: LFUSE = E2
|
||||
# 512 byte boot
|
||||
lilypad_isp: EFUSE = 04
|
||||
lilypad_isp: isp
|
||||
|
||||
lilypad_resonator: TARGET = lilypad_resonator
|
||||
lilypad_resonator: MCU_TARGET = atmega168
|
||||
lilypad_resonator: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
lilypad_resonator: AVR_FREQ = 8000000L
|
||||
lilypad_resonator: $(PROGRAM)_lilypad_resonator.hex
|
||||
lilypad_resonator: $(PROGRAM)_lilypad_resonator.lst
|
||||
|
||||
lilypad_resonator_isp: lilypad_resonator
|
||||
lilypad_resonator_isp: TARGET = lilypad_resonator
|
||||
# 2.7V brownout
|
||||
lilypad_resonator_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
lilypad_resonator_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
lilypad_resonator_isp: EFUSE = 04
|
||||
lilypad_resonator_isp: isp
|
||||
|
||||
pro8: TARGET = pro_8MHz
|
||||
pro8: MCU_TARGET = atmega168
|
||||
pro8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro8: AVR_FREQ = 8000000L
|
||||
pro8: $(PROGRAM)_pro_8MHz.hex
|
||||
pro8: $(PROGRAM)_pro_8MHz.lst
|
||||
|
||||
pro8_isp: pro8
|
||||
pro8_isp: TARGET = pro_8MHz
|
||||
# 2.7V brownout
|
||||
pro8_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro8_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro8_isp: EFUSE = 04
|
||||
pro8_isp: isp
|
||||
|
||||
atmega328_pro8: TARGET = atmega328_pro_8MHz
|
||||
atmega328_pro8: MCU_TARGET = atmega328p
|
||||
atmega328_pro8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
atmega328_pro8: AVR_FREQ = 8000000L
|
||||
atmega328_pro8: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
atmega328_pro8: $(PROGRAM)_atmega328_pro_8MHz.hex
|
||||
atmega328_pro8: $(PROGRAM)_atmega328_pro_8MHz.lst
|
||||
|
||||
atmega328_pro8_isp: atmega328_pro8
|
||||
atmega328_pro8_isp: TARGET = atmega328_pro_8MHz
|
||||
atmega328_pro8_isp: MCU_TARGET = atmega328p
|
||||
# 512 byte boot, SPIEN
|
||||
atmega328_pro8_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega328_pro8_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega328_pro8_isp: EFUSE = DE
|
||||
atmega328_pro8_isp: isp
|
||||
|
||||
# 1MHz clocked platforms
|
||||
#
|
||||
# These are capable of 9600 baud
|
||||
#
|
||||
|
||||
luminet: TARGET = luminet
|
||||
luminet: MCU_TARGET = attiny84
|
||||
luminet: CFLAGS += '-DLED_START_FLASHES=3' '-DSOFT_UART' '-DBAUD_RATE=9600'
|
||||
luminet: CFLAGS += '-DVIRTUAL_BOOT_PARTITION'
|
||||
luminet: AVR_FREQ = 1000000L
|
||||
luminet: LDSECTIONS = -Wl,--section-start=.text=0x1d00 -Wl,--section-start=.version=0x1efe
|
||||
luminet: $(PROGRAM)_luminet.hex
|
||||
luminet: $(PROGRAM)_luminet.lst
|
||||
|
||||
luminet_isp: luminet
|
||||
luminet_isp: TARGET = luminet
|
||||
luminet_isp: MCU_TARGET = attiny84
|
||||
# Brownout disabled
|
||||
luminet_isp: HFUSE = DF
|
||||
# 1MHz internal oscillator, slowly rising power
|
||||
luminet_isp: LFUSE = 62
|
||||
# Self-programming enable
|
||||
luminet_isp: EFUSE = FE
|
||||
luminet_isp: isp
|
||||
|
||||
#
|
||||
# Generic build instructions
|
||||
#
|
||||
#
|
||||
|
||||
isp: $(TARGET)
|
||||
$(ISPFUSES)
|
||||
$(ISPFLASH)
|
||||
|
||||
isp-stk500: $(PROGRAM)_$(TARGET).hex
|
||||
$(STK500-1)
|
||||
$(STK500-2)
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(SIZE) $@
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.elf *.lst *.map *.sym *.lss *.eep *.srec *.bin *.hex
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex $< $@
|
||||
|
||||
%.srec: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O srec $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O binary $< $@
|
||||
848
BootLoaders/AtmegaMultiBoot/Source/boot.h
Normal file
848
BootLoaders/AtmegaMultiBoot/Source/boot.h
Normal file
@@ -0,0 +1,848 @@
|
||||
/* Modified to use out for SPM access
|
||||
** Peter Knight, Optiboot project http://optiboot.googlecode.com
|
||||
**
|
||||
** Todo: Tidy up
|
||||
**
|
||||
** "_short" routines execute 1 cycle faster and use 1 less word of flash
|
||||
** by using "out" instruction instead of "sts".
|
||||
**
|
||||
** Additional elpm variants that trust the value of RAMPZ
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Eric B. Weddington
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
/* $Id: boot.h,v 1.27.2.3 2008/09/30 13:58:48 arcanum Exp $ */
|
||||
|
||||
#ifndef _AVR_BOOT_H_
|
||||
#define _AVR_BOOT_H_ 1
|
||||
|
||||
/** \file */
|
||||
/** \defgroup avr_boot <avr/boot.h>: Bootloader Support Utilities
|
||||
\code
|
||||
#include <avr/io.h>
|
||||
#include <avr/boot.h>
|
||||
\endcode
|
||||
|
||||
The macros in this module provide a C language interface to the
|
||||
bootloader support functionality of certain AVR processors. These
|
||||
macros are designed to work with all sizes of flash memory.
|
||||
|
||||
Global interrupts are not automatically disabled for these macros. It
|
||||
is left up to the programmer to do this. See the code example below.
|
||||
Also see the processor datasheet for caveats on having global interrupts
|
||||
enabled during writing of the Flash.
|
||||
|
||||
\note Not all AVR processors provide bootloader support. See your
|
||||
processor datasheet to see if it provides bootloader support.
|
||||
|
||||
\todo From email with Marek: On smaller devices (all except ATmega64/128),
|
||||
__SPM_REG is in the I/O space, accessible with the shorter "in" and "out"
|
||||
instructions - since the boot loader has a limited size, this could be an
|
||||
important optimization.
|
||||
|
||||
\par API Usage Example
|
||||
The following code shows typical usage of the boot API.
|
||||
|
||||
\code
|
||||
#include <inttypes.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
void boot_program_page (uint32_t page, uint8_t *buf)
|
||||
{
|
||||
uint16_t i;
|
||||
uint8_t sreg;
|
||||
|
||||
// Disable interrupts.
|
||||
|
||||
sreg = SREG;
|
||||
cli();
|
||||
|
||||
eeprom_busy_wait ();
|
||||
|
||||
boot_page_erase (page);
|
||||
boot_spm_busy_wait (); // Wait until the memory is erased.
|
||||
|
||||
for (i=0; i<SPM_PAGESIZE; i+=2)
|
||||
{
|
||||
// Set up little-endian word.
|
||||
|
||||
uint16_t w = *buf++;
|
||||
w += (*buf++) << 8;
|
||||
|
||||
boot_page_fill (page + i, w);
|
||||
}
|
||||
|
||||
boot_page_write (page); // Store buffer in flash page.
|
||||
boot_spm_busy_wait(); // Wait until the memory is written.
|
||||
|
||||
// Reenable RWW-section again. We need this if we want to jump back
|
||||
// to the application after bootloading.
|
||||
|
||||
boot_rww_enable ();
|
||||
|
||||
// Re-enable interrupts (if they were ever enabled).
|
||||
|
||||
SREG = sreg;
|
||||
}\endcode */
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/io.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Check for SPM Control Register in processor. */
|
||||
#if defined (SPMCSR)
|
||||
# define __SPM_REG SPMCSR
|
||||
#elif defined (SPMCR)
|
||||
# define __SPM_REG SPMCR
|
||||
#else
|
||||
# error AVR processor does not provide bootloader support!
|
||||
#endif
|
||||
|
||||
|
||||
/* Check for SPM Enable bit. */
|
||||
#if defined(SPMEN)
|
||||
# define __SPM_ENABLE SPMEN
|
||||
#elif defined(SELFPRGEN)
|
||||
# define __SPM_ENABLE SELFPRGEN
|
||||
#else
|
||||
# error Cannot find SPM Enable bit definition!
|
||||
#endif
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def BOOTLOADER_SECTION
|
||||
|
||||
Used to declare a function or variable to be placed into a
|
||||
new section called .bootloader. This section and its contents
|
||||
can then be relocated to any address (such as the bootloader
|
||||
NRWW area) at link-time. */
|
||||
|
||||
#define BOOTLOADER_SECTION __attribute__ ((section (".bootloader")))
|
||||
|
||||
/* Create common bit definitions. */
|
||||
#ifdef ASB
|
||||
#define __COMMON_ASB ASB
|
||||
#else
|
||||
#define __COMMON_ASB RWWSB
|
||||
#endif
|
||||
|
||||
#ifdef ASRE
|
||||
#define __COMMON_ASRE ASRE
|
||||
#else
|
||||
#define __COMMON_ASRE RWWSRE
|
||||
#endif
|
||||
|
||||
/* Define the bit positions of the Boot Lock Bits. */
|
||||
|
||||
#define BLB12 5
|
||||
#define BLB11 4
|
||||
#define BLB02 3
|
||||
#define BLB01 2
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_spm_interrupt_enable()
|
||||
Enable the SPM interrupt. */
|
||||
|
||||
#define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_spm_interrupt_disable()
|
||||
Disable the SPM interrupt. */
|
||||
|
||||
#define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_is_spm_interrupt()
|
||||
Check if the SPM interrupt is enabled. */
|
||||
|
||||
#define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_rww_busy()
|
||||
Check if the RWW section is busy. */
|
||||
|
||||
#define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_spm_busy()
|
||||
Check if the SPM instruction is busy. */
|
||||
|
||||
#define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(__SPM_ENABLE))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_spm_busy_wait()
|
||||
Wait while the SPM instruction is busy. */
|
||||
|
||||
#define boot_spm_busy_wait() do{}while(boot_spm_busy())
|
||||
|
||||
#define __BOOT_PAGE_ERASE (_BV(__SPM_ENABLE) | _BV(PGERS))
|
||||
#define __BOOT_PAGE_WRITE (_BV(__SPM_ENABLE) | _BV(PGWRT))
|
||||
#define __BOOT_PAGE_FILL _BV(__SPM_ENABLE)
|
||||
#define __BOOT_RWW_ENABLE (_BV(__SPM_ENABLE) | _BV(__COMMON_ASRE))
|
||||
#define __BOOT_LOCK_BITS_SET (_BV(__SPM_ENABLE) | _BV(BLBSET))
|
||||
|
||||
#define __boot_page_fill_short(address, data) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r0, %3\n\t" \
|
||||
"out %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
"clr r1\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_FILL), \
|
||||
"z" ((uint16_t)address), \
|
||||
"r" ((uint16_t)data) \
|
||||
: "r0" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_fill_normal(address, data) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r0, %3\n\t" \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
"clr r1\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_FILL), \
|
||||
"z" ((uint16_t)address), \
|
||||
"r" ((uint16_t)data) \
|
||||
: "r0" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_fill_alternate(address, data)\
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r0, %3\n\t" \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
".word 0xffff\n\t" \
|
||||
"nop\n\t" \
|
||||
"clr r1\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_FILL), \
|
||||
"z" ((uint16_t)address), \
|
||||
"r" ((uint16_t)data) \
|
||||
: "r0" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_fill_extended(address, data) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r0, %4\n\t" \
|
||||
"movw r30, %A3\n\t" \
|
||||
"sts %1, %C3\n\t" \
|
||||
"sts %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
"clr r1\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_MEM_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_FILL), \
|
||||
"r" ((uint32_t)address), \
|
||||
"r" ((uint16_t)data) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_fill_extended_short(address, data) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r0, %4\n\t" \
|
||||
"movw r30, %A3\n\t" \
|
||||
"out %1, %C3\n\t" \
|
||||
"out %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
"clr r1\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_IO_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_FILL), \
|
||||
"r" ((uint32_t)address), \
|
||||
"r" ((uint16_t)data) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_erase_short(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"out %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_ERASE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
|
||||
#define __boot_page_erase_normal(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_ERASE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_erase_alternate(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
".word 0xffff\n\t" \
|
||||
"nop\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_ERASE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_erase_extended(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r30, %A3\n\t" \
|
||||
"sts %1, %C3\n\t" \
|
||||
"sts %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_MEM_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_ERASE), \
|
||||
"r" ((uint32_t)address) \
|
||||
: "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
#define __boot_page_erase_extended_short(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r30, %A3\n\t" \
|
||||
"out %1, %C3\n\t" \
|
||||
"out %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_IO_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_ERASE), \
|
||||
"r" ((uint32_t)address) \
|
||||
: "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_write_short(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"out %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_WRITE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_write_normal(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_WRITE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_write_alternate(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
".word 0xffff\n\t" \
|
||||
"nop\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_WRITE), \
|
||||
"z" ((uint16_t)address) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_page_write_extended(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r30, %A3\n\t" \
|
||||
"sts %1, %C3\n\t" \
|
||||
"sts %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_MEM_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_WRITE), \
|
||||
"r" ((uint32_t)address) \
|
||||
: "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
#define __boot_page_write_extended_short(address) \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"movw r30, %A3\n\t" \
|
||||
"out %1, %C3\n\t" \
|
||||
"out %0, %2\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"i" (_SFR_IO_ADDR(RAMPZ)), \
|
||||
"r" ((uint8_t)__BOOT_PAGE_WRITE), \
|
||||
"r" ((uint32_t)address) \
|
||||
: "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_rww_enable_short() \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"out %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_RWW_ENABLE) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_rww_enable() \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_RWW_ENABLE) \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_rww_enable_alternate() \
|
||||
(__extension__({ \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
".word 0xffff\n\t" \
|
||||
"nop\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_RWW_ENABLE) \
|
||||
); \
|
||||
}))
|
||||
|
||||
/* From the mega16/mega128 data sheets (maybe others):
|
||||
|
||||
Bits by SPM To set the Boot Loader Lock bits, write the desired data to
|
||||
R0, write "X0001001" to SPMCR and execute SPM within four clock cycles
|
||||
after writing SPMCR. The only accessible Lock bits are the Boot Lock bits
|
||||
that may prevent the Application and Boot Loader section from any
|
||||
software update by the MCU.
|
||||
|
||||
If bits 5..2 in R0 are cleared (zero), the corresponding Boot Lock bit
|
||||
will be programmed if an SPM instruction is executed within four cycles
|
||||
after BLBSET and SPMEN (or SELFPRGEN) are set in SPMCR. The Z-pointer is
|
||||
don't care during this operation, but for future compatibility it is
|
||||
recommended to load the Z-pointer with $0001 (same as used for reading the
|
||||
Lock bits). For future compatibility It is also recommended to set bits 7,
|
||||
6, 1, and 0 in R0 to 1 when writing the Lock bits. When programming the
|
||||
Lock bits the entire Flash can be read during the operation. */
|
||||
|
||||
#define __boot_lock_bits_set_short(lock_bits) \
|
||||
(__extension__({ \
|
||||
uint8_t value = (uint8_t)(~(lock_bits)); \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"ldi r30, 1\n\t" \
|
||||
"ldi r31, 0\n\t" \
|
||||
"mov r0, %2\n\t" \
|
||||
"out %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
|
||||
"r" (value) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_lock_bits_set(lock_bits) \
|
||||
(__extension__({ \
|
||||
uint8_t value = (uint8_t)(~(lock_bits)); \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"ldi r30, 1\n\t" \
|
||||
"ldi r31, 0\n\t" \
|
||||
"mov r0, %2\n\t" \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
|
||||
"r" (value) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
#define __boot_lock_bits_set_alternate(lock_bits) \
|
||||
(__extension__({ \
|
||||
uint8_t value = (uint8_t)(~(lock_bits)); \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"ldi r30, 1\n\t" \
|
||||
"ldi r31, 0\n\t" \
|
||||
"mov r0, %2\n\t" \
|
||||
"sts %0, %1\n\t" \
|
||||
"spm\n\t" \
|
||||
".word 0xffff\n\t" \
|
||||
"nop\n\t" \
|
||||
: \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
|
||||
"r" (value) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
}))
|
||||
|
||||
/*
|
||||
Reading lock and fuse bits:
|
||||
|
||||
Similarly to writing the lock bits above, set BLBSET and SPMEN (or
|
||||
SELFPRGEN) bits in __SPMREG, and then (within four clock cycles) issue an
|
||||
LPM instruction.
|
||||
|
||||
Z address: contents:
|
||||
0x0000 low fuse bits
|
||||
0x0001 lock bits
|
||||
0x0002 extended fuse bits
|
||||
0x0003 high fuse bits
|
||||
|
||||
Sounds confusing, doesn't it?
|
||||
|
||||
Unlike the macros in pgmspace.h, no need to care for non-enhanced
|
||||
cores here as these old cores do not provide SPM support anyway.
|
||||
*/
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def GET_LOW_FUSE_BITS
|
||||
address to read the low fuse bits, using boot_lock_fuse_bits_get
|
||||
*/
|
||||
#define GET_LOW_FUSE_BITS (0x0000)
|
||||
/** \ingroup avr_boot
|
||||
\def GET_LOCK_BITS
|
||||
address to read the lock bits, using boot_lock_fuse_bits_get
|
||||
*/
|
||||
#define GET_LOCK_BITS (0x0001)
|
||||
/** \ingroup avr_boot
|
||||
\def GET_EXTENDED_FUSE_BITS
|
||||
address to read the extended fuse bits, using boot_lock_fuse_bits_get
|
||||
*/
|
||||
#define GET_EXTENDED_FUSE_BITS (0x0002)
|
||||
/** \ingroup avr_boot
|
||||
\def GET_HIGH_FUSE_BITS
|
||||
address to read the high fuse bits, using boot_lock_fuse_bits_get
|
||||
*/
|
||||
#define GET_HIGH_FUSE_BITS (0x0003)
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_lock_fuse_bits_get(address)
|
||||
|
||||
Read the lock or fuse bits at \c address.
|
||||
|
||||
Parameter \c address can be any of GET_LOW_FUSE_BITS,
|
||||
GET_LOCK_BITS, GET_EXTENDED_FUSE_BITS, or GET_HIGH_FUSE_BITS.
|
||||
|
||||
\note The lock and fuse bits returned are the physical values,
|
||||
i.e. a bit returned as 0 means the corresponding fuse or lock bit
|
||||
is programmed.
|
||||
*/
|
||||
#define boot_lock_fuse_bits_get_short(address) \
|
||||
(__extension__({ \
|
||||
uint8_t __result; \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"ldi r30, %3\n\t" \
|
||||
"ldi r31, 0\n\t" \
|
||||
"out %1, %2\n\t" \
|
||||
"lpm %0, Z\n\t" \
|
||||
: "=r" (__result) \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
|
||||
"M" (address) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
__result; \
|
||||
}))
|
||||
|
||||
#define boot_lock_fuse_bits_get(address) \
|
||||
(__extension__({ \
|
||||
uint8_t __result; \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"ldi r30, %3\n\t" \
|
||||
"ldi r31, 0\n\t" \
|
||||
"sts %1, %2\n\t" \
|
||||
"lpm %0, Z\n\t" \
|
||||
: "=r" (__result) \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
|
||||
"M" (address) \
|
||||
: "r0", "r30", "r31" \
|
||||
); \
|
||||
__result; \
|
||||
}))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_signature_byte_get(address)
|
||||
|
||||
Read the Signature Row byte at \c address. For some MCU types,
|
||||
this function can also retrieve the factory-stored oscillator
|
||||
calibration bytes.
|
||||
|
||||
Parameter \c address can be 0-0x1f as documented by the datasheet.
|
||||
\note The values are MCU type dependent.
|
||||
*/
|
||||
|
||||
#define __BOOT_SIGROW_READ (_BV(__SPM_ENABLE) | _BV(SIGRD))
|
||||
|
||||
#define boot_signature_byte_get_short(addr) \
|
||||
(__extension__({ \
|
||||
uint16_t __addr16 = (uint16_t)(addr); \
|
||||
uint8_t __result; \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"out %1, %2\n\t" \
|
||||
"lpm %0, Z" "\n\t" \
|
||||
: "=r" (__result) \
|
||||
: "i" (_SFR_IO_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t) __BOOT_SIGROW_READ), \
|
||||
"z" (__addr16) \
|
||||
); \
|
||||
__result; \
|
||||
}))
|
||||
|
||||
#define boot_signature_byte_get(addr) \
|
||||
(__extension__({ \
|
||||
uint16_t __addr16 = (uint16_t)(addr); \
|
||||
uint8_t __result; \
|
||||
__asm__ __volatile__ \
|
||||
( \
|
||||
"sts %1, %2\n\t" \
|
||||
"lpm %0, Z" "\n\t" \
|
||||
: "=r" (__result) \
|
||||
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
|
||||
"r" ((uint8_t) __BOOT_SIGROW_READ), \
|
||||
"z" (__addr16) \
|
||||
); \
|
||||
__result; \
|
||||
}))
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_page_fill(address, data)
|
||||
|
||||
Fill the bootloader temporary page buffer for flash
|
||||
address with data word.
|
||||
|
||||
\note The address is a byte address. The data is a word. The AVR
|
||||
writes data to the buffer a word at a time, but addresses the buffer
|
||||
per byte! So, increment your address by 2 between calls, and send 2
|
||||
data bytes in a word format! The LSB of the data is written to the lower
|
||||
address; the MSB of the data is written to the higher address.*/
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_page_erase(address)
|
||||
|
||||
Erase the flash page that contains address.
|
||||
|
||||
\note address is a byte address in flash, not a word address. */
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_page_write(address)
|
||||
|
||||
Write the bootloader temporary page buffer
|
||||
to flash page that contains address.
|
||||
|
||||
\note address is a byte address in flash, not a word address. */
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_rww_enable()
|
||||
|
||||
Enable the Read-While-Write memory section. */
|
||||
|
||||
/** \ingroup avr_boot
|
||||
\def boot_lock_bits_set(lock_bits)
|
||||
|
||||
Set the bootloader lock bits.
|
||||
|
||||
\param lock_bits A mask of which Boot Loader Lock Bits to set.
|
||||
|
||||
\note In this context, a 'set bit' will be written to a zero value.
|
||||
Note also that only BLBxx bits can be programmed by this command.
|
||||
|
||||
For example, to disallow the SPM instruction from writing to the Boot
|
||||
Loader memory section of flash, you would use this macro as such:
|
||||
|
||||
\code
|
||||
boot_lock_bits_set (_BV (BLB11));
|
||||
\endcode
|
||||
|
||||
\note Like any lock bits, the Boot Loader Lock Bits, once set,
|
||||
cannot be cleared again except by a chip erase which will in turn
|
||||
also erase the boot loader itself. */
|
||||
|
||||
/* Normal versions of the macros use 16-bit addresses.
|
||||
Extended versions of the macros use 32-bit addresses.
|
||||
Alternate versions of the macros use 16-bit addresses and require special
|
||||
instruction sequences after LPM.
|
||||
|
||||
FLASHEND is defined in the ioXXXX.h file.
|
||||
USHRT_MAX is defined in <limits.h>. */
|
||||
|
||||
#if defined(__AVR_ATmega161__) || defined(__AVR_ATmega163__) \
|
||||
|| defined(__AVR_ATmega323__)
|
||||
|
||||
/* Alternate: ATmega161/163/323 and 16 bit address */
|
||||
#define boot_page_fill(address, data) __boot_page_fill_alternate(address, data)
|
||||
#define boot_page_erase(address) __boot_page_erase_alternate(address)
|
||||
#define boot_page_write(address) __boot_page_write_alternate(address)
|
||||
#define boot_rww_enable() __boot_rww_enable_alternate()
|
||||
#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_alternate(lock_bits)
|
||||
|
||||
#elif (FLASHEND > USHRT_MAX)
|
||||
|
||||
/* Extended: >16 bit address */
|
||||
#define boot_page_fill(address, data) __boot_page_fill_extended_short(address, data)
|
||||
#define boot_page_erase(address) __boot_page_erase_extended_short(address)
|
||||
#define boot_page_write(address) __boot_page_write_extended_short(address)
|
||||
#define boot_rww_enable() __boot_rww_enable_short()
|
||||
#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits)
|
||||
|
||||
#else
|
||||
|
||||
/* Normal: 16 bit address */
|
||||
#define boot_page_fill(address, data) __boot_page_fill_short(address, data)
|
||||
#define boot_page_erase(address) __boot_page_erase_short(address)
|
||||
#define boot_page_write(address) __boot_page_write_short(address)
|
||||
#define boot_rww_enable() __boot_rww_enable_short()
|
||||
#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits)
|
||||
|
||||
#endif
|
||||
|
||||
/** \ingroup avr_boot
|
||||
|
||||
Same as boot_page_fill() except it waits for eeprom and spm operations to
|
||||
complete before filling the page. */
|
||||
|
||||
#define boot_page_fill_safe(address, data) \
|
||||
do { \
|
||||
boot_spm_busy_wait(); \
|
||||
eeprom_busy_wait(); \
|
||||
boot_page_fill(address, data); \
|
||||
} while (0)
|
||||
|
||||
/** \ingroup avr_boot
|
||||
|
||||
Same as boot_page_erase() except it waits for eeprom and spm operations to
|
||||
complete before erasing the page. */
|
||||
|
||||
#define boot_page_erase_safe(address) \
|
||||
do { \
|
||||
boot_spm_busy_wait(); \
|
||||
eeprom_busy_wait(); \
|
||||
boot_page_erase (address); \
|
||||
} while (0)
|
||||
|
||||
/** \ingroup avr_boot
|
||||
|
||||
Same as boot_page_write() except it waits for eeprom and spm operations to
|
||||
complete before writing the page. */
|
||||
|
||||
#define boot_page_write_safe(address) \
|
||||
do { \
|
||||
boot_spm_busy_wait(); \
|
||||
eeprom_busy_wait(); \
|
||||
boot_page_write (address); \
|
||||
} while (0)
|
||||
|
||||
/** \ingroup avr_boot
|
||||
|
||||
Same as boot_rww_enable() except waits for eeprom and spm operations to
|
||||
complete before enabling the RWW mameory. */
|
||||
|
||||
#define boot_rww_enable_safe() \
|
||||
do { \
|
||||
boot_spm_busy_wait(); \
|
||||
eeprom_busy_wait(); \
|
||||
boot_rww_enable(); \
|
||||
} while (0)
|
||||
|
||||
/** \ingroup avr_boot
|
||||
|
||||
Same as boot_lock_bits_set() except waits for eeprom and spm operations to
|
||||
complete before setting the lock bits. */
|
||||
|
||||
#define boot_lock_bits_set_safe(lock_bits) \
|
||||
do { \
|
||||
boot_spm_busy_wait(); \
|
||||
eeprom_busy_wait(); \
|
||||
boot_lock_bits_set (lock_bits); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _AVR_BOOT_H_ */
|
||||
891
BootLoaders/AtmegaMultiBoot/Source/optiboot.c
Normal file
891
BootLoaders/AtmegaMultiBoot/Source/optiboot.c
Normal file
@@ -0,0 +1,891 @@
|
||||
/**********************************************************/
|
||||
/* Optiboot bootloader for Arduino */
|
||||
/* */
|
||||
/* http://optiboot.googlecode.com */
|
||||
/* */
|
||||
/* Arduino-maintained version : See README.TXT */
|
||||
/* http://code.google.com/p/arduino/ */
|
||||
/* It is the intent that changes not relevant to the */
|
||||
/* Arduino production envionment get moved from the */
|
||||
/* optiboot project to the arduino project in "lumps." */
|
||||
/* */
|
||||
/* Heavily optimised bootloader that is faster and */
|
||||
/* smaller than the Arduino standard bootloader */
|
||||
/* */
|
||||
/* Enhancements: */
|
||||
/* Fits in 512 bytes, saving 1.5K of code space */
|
||||
/* Background page erasing speeds up programming */
|
||||
/* Higher baud rate speeds up programming */
|
||||
/* Written almost entirely in C */
|
||||
/* Customisable timeout with accurate timeconstant */
|
||||
/* Optional virtual UART. No hardware UART required. */
|
||||
/* Optional virtual boot partition for devices without. */
|
||||
/* */
|
||||
/* What you lose: */
|
||||
/* Implements a skeleton STK500 protocol which is */
|
||||
/* missing several features including EEPROM */
|
||||
/* programming and non-page-aligned writes */
|
||||
/* High baud rate breaks compatibility with standard */
|
||||
/* Arduino flash settings */
|
||||
/* */
|
||||
/* Fully supported: */
|
||||
/* ATmega168 based devices (Diecimila etc) */
|
||||
/* ATmega328P based devices (Duemilanove etc) */
|
||||
/* */
|
||||
/* Beta test (believed working.) */
|
||||
/* ATmega8 based devices (Arduino legacy) */
|
||||
/* ATmega328 non-picopower devices */
|
||||
/* ATmega644P based devices (Sanguino) */
|
||||
/* ATmega1284P based devices */
|
||||
/* */
|
||||
/* Alpha test */
|
||||
/* ATmega1280 based devices (Arduino Mega) */
|
||||
/* */
|
||||
/* Work in progress: */
|
||||
/* ATtiny84 based devices (Luminet) */
|
||||
/* */
|
||||
/* Does not support: */
|
||||
/* USB based devices (eg. Teensy) */
|
||||
/* */
|
||||
/* Assumptions: */
|
||||
/* The code makes several assumptions that reduce the */
|
||||
/* code size. They are all true after a hardware reset, */
|
||||
/* but may not be true if the bootloader is called by */
|
||||
/* other means or on other hardware. */
|
||||
/* No interrupts can occur */
|
||||
/* UART and Timer 1 are set to their reset state */
|
||||
/* SP points to RAMEND */
|
||||
/* */
|
||||
/* Code builds on code, libraries and optimisations from: */
|
||||
/* stk500boot.c by Jason P. Kyle */
|
||||
/* Arduino bootloader http://arduino.cc */
|
||||
/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
|
||||
/* avr-libc project http://nongnu.org/avr-libc */
|
||||
/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
|
||||
/* AVR305 Atmel Application Note */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it */
|
||||
/* and/or modify it under the terms of the GNU General */
|
||||
/* Public License as published by the Free Software */
|
||||
/* Foundation; either version 2 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will */
|
||||
/* be useful, but WITHOUT ANY WARRANTY; without even the */
|
||||
/* implied warranty of MERCHANTABILITY or FITNESS FOR A */
|
||||
/* PARTICULAR PURPOSE. See the GNU General Public */
|
||||
/* License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General */
|
||||
/* Public License along with this program; if not, write */
|
||||
/* to the Free Software Foundation, Inc., */
|
||||
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
/* */
|
||||
/* Licence can be viewed at */
|
||||
/* http://www.fsf.org/licenses/gpl.txt */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
|
||||
/**********************************************************/
|
||||
/* */
|
||||
/* Optional defines: */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
/* */
|
||||
/* BIG_BOOT: */
|
||||
/* Build a 1k bootloader, not 512 bytes. This turns on */
|
||||
/* extra functionality. */
|
||||
/* */
|
||||
/* BAUD_RATE: */
|
||||
/* Set bootloader baud rate. */
|
||||
/* */
|
||||
/* LUDICROUS_SPEED: */
|
||||
/* 230400 baud :-) */
|
||||
/* */
|
||||
/* SOFT_UART: */
|
||||
/* Use AVR305 soft-UART instead of hardware UART. */
|
||||
/* */
|
||||
/* LED_START_FLASHES: */
|
||||
/* Number of LED flashes on bootup. */
|
||||
/* */
|
||||
/* LED_DATA_FLASH: */
|
||||
/* Flash LED when transferring data. For boards without */
|
||||
/* TX or RX LEDs, or for people who like blinky lights. */
|
||||
/* */
|
||||
/* SUPPORT_EEPROM: */
|
||||
/* Support reading and writing from EEPROM. This is not */
|
||||
/* used by Arduino, so off by default. */
|
||||
/* */
|
||||
/* TIMEOUT_MS: */
|
||||
/* Bootloader timeout period, in milliseconds. */
|
||||
/* 500,1000,2000,4000,8000 supported. */
|
||||
/* */
|
||||
/* UART: */
|
||||
/* UART number (0..n) for devices with more than */
|
||||
/* one hardware uart (644P, 1284P, etc) */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
/**********************************************************/
|
||||
/* Version Numbers! */
|
||||
/* */
|
||||
/* Arduino Optiboot now includes this Version number in */
|
||||
/* the source and object code. */
|
||||
/* */
|
||||
/* Version 3 was released as zip from the optiboot */
|
||||
/* repository and was distributed with Arduino 0022. */
|
||||
/* Version 4 starts with the arduino repository commit */
|
||||
/* that brought the arduino repository up-to-date with */
|
||||
/* the optiboot source tree changes since v3. */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
/**********************************************************/
|
||||
/* Edit History: */
|
||||
/* */
|
||||
/* Nov 2012 */
|
||||
/* Specific version for 9x voice module */
|
||||
/* by Mike Blandford */
|
||||
/* Mar 2012 */
|
||||
/* 4.5 WestfW: add infrastructure for non-zero UARTS. */
|
||||
/* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */
|
||||
/* Jan 2012: */
|
||||
/* 4.5 WestfW: fix NRWW value for m1284. */
|
||||
/* 4.4 WestfW: use attribute OS_main instead of naked for */
|
||||
/* main(). This allows optimizations that we */
|
||||
/* count on, which are prohibited in naked */
|
||||
/* functions due to PR42240. (keeps us less */
|
||||
/* than 512 bytes when compiler is gcc4.5 */
|
||||
/* (code from 4.3.2 remains the same.) */
|
||||
/* 4.4 WestfW and Maniacbug: Add m1284 support. This */
|
||||
/* does not change the 328 binary, so the */
|
||||
/* version number didn't change either. (?) */
|
||||
/* June 2011: */
|
||||
/* 4.4 WestfW: remove automatic soft_uart detect (didn't */
|
||||
/* know what it was doing or why.) Added a */
|
||||
/* check of the calculated BRG value instead. */
|
||||
/* Version stays 4.4; existing binaries are */
|
||||
/* not changed. */
|
||||
/* 4.4 WestfW: add initialization of address to keep */
|
||||
/* the compiler happy. Change SC'ed targets. */
|
||||
/* Return the SW version via READ PARAM */
|
||||
/* 4.3 WestfW: catch framing errors in getch(), so that */
|
||||
/* AVRISP works without HW kludges. */
|
||||
/* http://code.google.com/p/arduino/issues/detail?id=368n*/
|
||||
/* 4.2 WestfW: reduce code size, fix timeouts, change */
|
||||
/* verifySpace to use WDT instead of appstart */
|
||||
/* 4.1 WestfW: put version number in binary. */
|
||||
/**********************************************************/
|
||||
|
||||
#define OPTIBOOT_MAJVER 4
|
||||
#define OPTIBOOT_MINVER 5
|
||||
|
||||
#define MULTI_CALLED 1
|
||||
|
||||
#define MAKESTR(a) #a
|
||||
#define MAKEVER(a, b) MAKESTR(a*256+b)
|
||||
|
||||
//asm(" .section .version\n"
|
||||
// "optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
|
||||
// " .section .text\n");
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
// <avr/boot.h> uses sts instructions, but this version uses out instructions
|
||||
// This saves cycles and program memory.
|
||||
#include "boot.h"
|
||||
|
||||
|
||||
// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
|
||||
|
||||
#include "pin_defs.h"
|
||||
#include "stk500.h"
|
||||
|
||||
#ifndef LED_START_FLASHES
|
||||
#define LED_START_FLASHES 0
|
||||
#endif
|
||||
|
||||
#ifdef LUDICROUS_SPEED
|
||||
#define BAUD_RATE 230400L
|
||||
#endif
|
||||
|
||||
/* set the UART baud rate defaults */
|
||||
#ifndef BAUD_RATE
|
||||
#if F_CPU >= 8000000L
|
||||
#define BAUD_RATE 38400L // Highest rate Avrdude win32 will support
|
||||
#elsif F_CPU >= 1000000L
|
||||
#define BAUD_RATE 9600L // 19200 also supported, but with significant error
|
||||
#elsif F_CPU >= 128000L
|
||||
#define BAUD_RATE 4800L // Good for 128kHz internal RC
|
||||
#else
|
||||
#define BAUD_RATE 1200L // Good even at 32768Hz
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UART
|
||||
#define UART 0
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Switch in soft UART for hard baud rates */
|
||||
/*
|
||||
* I don't understand what this was supposed to accomplish, where the
|
||||
* constant "280" came from, or why automatically (and perhaps unexpectedly)
|
||||
* switching to a soft uart is a good thing, so I'm undoing this in favor
|
||||
* of a range check using the same calc used to config the BRG...
|
||||
*/
|
||||
#if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz
|
||||
#ifndef SOFT_UART
|
||||
#define SOFT_UART
|
||||
#endif
|
||||
#endif
|
||||
#else // 0
|
||||
#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 > 250
|
||||
#error Unachievable baud rate (too slow) BAUD_RATE
|
||||
#endif // baud rate slow check
|
||||
#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 < 3
|
||||
#error Unachievable baud rate (too fast) BAUD_RATE
|
||||
#endif // baud rate fastn check
|
||||
#endif
|
||||
|
||||
/* Watchdog settings */
|
||||
#define WATCHDOG_OFF (0)
|
||||
#define WATCHDOG_16MS (_BV(WDE))
|
||||
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
|
||||
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#ifndef __AVR_ATmega8__
|
||||
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
|
||||
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
||||
#endif
|
||||
|
||||
/* Function Prototypes */
|
||||
/* The main function is in init9, which removes the interrupt vector table */
|
||||
/* we don't need. It is also 'naked', which means the compiler does not */
|
||||
/* generate any entry or exit code itself. */
|
||||
int main(void) __attribute__ ((OS_main)) __attribute__ ((noreturn)) __attribute__ ((section (".init9")));
|
||||
void putch(char);
|
||||
uint8_t getch(void);
|
||||
static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */
|
||||
void verifySpace();
|
||||
#if LED_START_FLASHES > 0
|
||||
static inline void flash_led(uint8_t);
|
||||
#endif
|
||||
uint8_t getLen();
|
||||
//static inline void watchdogReset();
|
||||
void watchdogConfig(uint8_t x);
|
||||
#ifdef SOFT_UART
|
||||
void uartDelay() __attribute__ ((naked));
|
||||
#endif
|
||||
static void appStart() ; // __attribute__ ((naked));
|
||||
|
||||
/*
|
||||
* NRWW memory
|
||||
* Addresses below NRWW (Non-Read-While-Write) can be programmed while
|
||||
* continuing to run code from flash, slightly speeding up programming
|
||||
* time. Beware that Atmel data sheets specify this as a WORD address,
|
||||
* while optiboot will be comparing against a 16-bit byte address. This
|
||||
* means that on a part with 128kB of memory, the upper part of the lower
|
||||
* 64k will get NRWW processing as well, even though it doesn't need it.
|
||||
* That's OK. In fact, you can disable the overlapping processing for
|
||||
* a part entirely by setting NRWWSTART to zero. This reduces code
|
||||
* space a bit, at the expense of being slightly slower, overall.
|
||||
*
|
||||
* RAMSTART should be self-explanatory. It's bigger on parts with a
|
||||
* lot of peripheral registers.
|
||||
*/
|
||||
#if defined(__AVR_ATmega168__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x3800)
|
||||
#elif defined(__AVR_ATmega328P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x7000)
|
||||
#elif defined(__AVR_ATmega328__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x7000)
|
||||
#elif defined (__AVR_ATmega644P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0xE000)
|
||||
// correct for a bug in avr-libc
|
||||
#undef SIGNATURE_2
|
||||
#define SIGNATURE_2 0x0A
|
||||
#elif defined (__AVR_ATmega1284P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0xE000)
|
||||
#elif defined(__AVR_ATtiny84__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x0000)
|
||||
#elif defined(__AVR_ATmega1280__)
|
||||
#define RAMSTART (0x200)
|
||||
#define NRWWSTART (0xE000)
|
||||
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x1800)
|
||||
#endif
|
||||
|
||||
/* C zero initialises all global variables. However, that requires */
|
||||
/* These definitions are NOT zero initialised, but that doesn't matter */
|
||||
/* This allows us to drop the zero init code, saving us memory */
|
||||
#define buff ((uint8_t*)(RAMSTART))
|
||||
#ifdef VIRTUAL_BOOT_PARTITION
|
||||
#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4))
|
||||
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle devices with up to 4 uarts (eg m1280.) Rather inelegantly.
|
||||
* Note that mega8 still needs special handling, because ubrr is handled
|
||||
* differently.
|
||||
*/
|
||||
#if UART == 0
|
||||
# define UART_SRA UCSR0A
|
||||
# define UART_SRB UCSR0B
|
||||
# define UART_SRC UCSR0C
|
||||
# define UART_SRL UBRR0L
|
||||
# define UART_UDR UDR0
|
||||
#elif UART == 1
|
||||
# define UART_SRA UCSR1A
|
||||
# define UART_SRB UCSR1B
|
||||
# define UART_SRC UCSR1C
|
||||
# define UART_SRL UBRR1L
|
||||
# define UART_UDR UDR1
|
||||
#elif UART == 2
|
||||
# define UART_SRA UCSR2A
|
||||
# define UART_SRB UCSR2B
|
||||
# define UART_SRC UCSR2C
|
||||
# define UART_SRL UBRR2L
|
||||
# define UART_UDR UDR2
|
||||
#elif UART == 3
|
||||
# define UART_SRA UCSR3A
|
||||
# define UART_SRB UCSR3B
|
||||
# define UART_SRC UCSR3C
|
||||
# define UART_SRL UBRR3L
|
||||
# define UART_UDR UDR3
|
||||
#endif
|
||||
|
||||
/* main program starts here */
|
||||
int main(void) {
|
||||
uint8_t ch;
|
||||
|
||||
/*
|
||||
* Making these local and in registers prevents the need for initializing
|
||||
* them, and also saves space because code no longer stores to memory.
|
||||
* (initializing address keeps the compiler happy, but isn't really
|
||||
* necessary, and uses 4 bytes of flash.)
|
||||
*/
|
||||
register uint16_t address = 0;
|
||||
|
||||
// After the zero init loop, this is the first code to run.
|
||||
//
|
||||
// This code makes the following assumptions:
|
||||
// No interrupts will execute
|
||||
// SP points to RAMEND
|
||||
// r1 contains zero
|
||||
//
|
||||
// If not, uncomment the following instructions:
|
||||
// cli();
|
||||
asm volatile ("clr __zero_reg__");
|
||||
#ifdef __AVR_ATmega8__
|
||||
SP=RAMEND; // This is done by hardware reset
|
||||
#endif
|
||||
|
||||
// Adaboot no-wait mod
|
||||
ch = MCUSR;
|
||||
MCUSR = 0;
|
||||
|
||||
// Here, if power on, wait 0.5 secs, then check for
|
||||
// serial Rx signal low, if so, stay in bootloader
|
||||
// else go to application
|
||||
|
||||
PORTD = 0xFF ;
|
||||
PORTB = 0x3C ;
|
||||
PORTC = 1 ;
|
||||
|
||||
if (ch & (_BV(PORF) | (_BV(EXTRF)) ) )
|
||||
{
|
||||
#ifdef MULTI_CALLED
|
||||
#if F_CPU == 12000000L
|
||||
TCNT1H = 256 - 8 ;
|
||||
#else
|
||||
#if F_CPU == 16000000L
|
||||
TCNT1H = 256 - 6 ;
|
||||
#else
|
||||
TCNT1H = 256 - 127 ;
|
||||
#endif
|
||||
TCNT1L = 0 ;
|
||||
#endif
|
||||
#else
|
||||
#if F_CPU == 12000000L
|
||||
TCNT1 = 65535-5859 ;
|
||||
#else
|
||||
#if F_CPU == 16000000L
|
||||
TCNT1 = 65535-7813 ;
|
||||
#else
|
||||
TCNT1 = 65535-32767 ;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
|
||||
TIFR1 = _BV(TOV1);
|
||||
while(!(TIFR1 & _BV(TOV1)))
|
||||
;
|
||||
TCCR1B = 0 ; // Stop timer
|
||||
uint8_t x ;
|
||||
x = PINB & 0x3C ;
|
||||
x |= PINC & 1 ;
|
||||
if ( x != 0x1D )
|
||||
{
|
||||
appStart() ; // Power on, go to voice application
|
||||
// if loaded
|
||||
}
|
||||
}
|
||||
|
||||
#if LED_START_FLASHES > 0
|
||||
// Set up Timer 1 for timeout counter
|
||||
TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
|
||||
#endif
|
||||
|
||||
#ifndef SOFT_UART
|
||||
UART_SRA = _BV(U2X0); //Double speed mode USART0
|
||||
UART_SRB = _BV(RXEN0) | _BV(TXEN0);
|
||||
UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
|
||||
// UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
|
||||
// Baudrate of 57600
|
||||
#if F_CPU == 12000000L
|
||||
UART_SRL = 25 ;
|
||||
#else
|
||||
#if F_CPU == 16000000L
|
||||
UART_SRL = 33 ;
|
||||
#else
|
||||
#ERROR Baud rate not available
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Set up watchdog to trigger after 500ms
|
||||
|
||||
|
||||
// watchdogConfig(WATCHDOG_1S);
|
||||
|
||||
/* Set LED pin as output */
|
||||
LED_DDR |= _BV(LED);
|
||||
|
||||
#ifdef SOFT_UART
|
||||
/* Set TX pin as output */
|
||||
UART_DDR |= _BV(UART_TX_BIT);
|
||||
#endif
|
||||
|
||||
#if LED_START_FLASHES > 0
|
||||
/* Flash onboard LED to signal entering of bootloader */
|
||||
flash_led(LED_START_FLASHES * 2);
|
||||
#endif
|
||||
|
||||
/* Forever loop */
|
||||
for (;;)
|
||||
{
|
||||
/* get character from UART */
|
||||
ch = getch();
|
||||
|
||||
if(ch == STK_GET_PARAMETER)
|
||||
{
|
||||
GPIOR0 = getch();
|
||||
verifySpace();
|
||||
if (GPIOR0 == 0x82)
|
||||
{
|
||||
/*
|
||||
* Send optiboot version as "minor SW version"
|
||||
*/
|
||||
putch(OPTIBOOT_MINVER);
|
||||
}
|
||||
else if (GPIOR0 == 0x81)
|
||||
{
|
||||
putch(OPTIBOOT_MAJVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* GET PARAMETER returns a generic 0x03 reply for
|
||||
* other parameters - enough to keep Avrdude happy
|
||||
*/
|
||||
putch(0x03);
|
||||
}
|
||||
}
|
||||
else if(ch == STK_SET_DEVICE) {
|
||||
// SET DEVICE is ignored
|
||||
getNch(20);
|
||||
}
|
||||
else if(ch == STK_SET_DEVICE_EXT)
|
||||
{
|
||||
// SET DEVICE EXT is ignored
|
||||
getNch(5);
|
||||
}
|
||||
else if(ch == STK_LOAD_ADDRESS)
|
||||
{
|
||||
// LOAD ADDRESS
|
||||
uint16_t newAddress;
|
||||
newAddress = getch() ;
|
||||
newAddress = (newAddress & 0xff) | (getch() << 8);
|
||||
#ifdef RAMPZ
|
||||
// Transfer top bit to RAMPZ
|
||||
RAMPZ = (newAddress & 0x8000) ? 1 : 0;
|
||||
#endif
|
||||
newAddress += newAddress; // Convert from word address to byte address
|
||||
address = newAddress;
|
||||
verifySpace();
|
||||
}
|
||||
else if(ch == STK_UNIVERSAL)
|
||||
{
|
||||
// UNIVERSAL command is ignored
|
||||
getNch(4);
|
||||
putch(0x00);
|
||||
}
|
||||
/* Write memory, length is big endian and is in bytes */
|
||||
else if(ch == STK_PROG_PAGE)
|
||||
{
|
||||
// PROGRAM PAGE - we support flash programming only, not EEPROM
|
||||
uint8_t *bufPtr;
|
||||
uint16_t addrPtr;
|
||||
register uint8_t length;
|
||||
|
||||
getch(); /* getlen() */
|
||||
length = getch();
|
||||
getch();
|
||||
|
||||
// If we are in RWW section, immediately start page erase
|
||||
if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
||||
|
||||
// While that is going on, read in page contents
|
||||
bufPtr = buff;
|
||||
do *bufPtr++ = getch();
|
||||
while (--length);
|
||||
|
||||
// If we are in NRWW section, page erase has to be delayed until now.
|
||||
// Todo: Take RAMPZ into account
|
||||
#ifdef MULTI_CALLED
|
||||
if (address < 0x7E00)
|
||||
#endif
|
||||
{
|
||||
if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
||||
}
|
||||
// Read command terminator, start reply
|
||||
verifySpace();
|
||||
|
||||
// If only a partial page is to be programmed, the erase might not be complete.
|
||||
// So check that here
|
||||
#ifdef MULTI_CALLED
|
||||
if (address < 0x7E00)
|
||||
#endif
|
||||
{
|
||||
boot_spm_busy_wait();
|
||||
|
||||
#ifdef VIRTUAL_BOOT_PARTITION
|
||||
if ((uint16_t)(void*)address == 0) {
|
||||
// This is the reset vector page. We need to live-patch the code so the
|
||||
// bootloader runs.
|
||||
//
|
||||
// Move RESET vector to WDT vector
|
||||
uint16_t vect = buff[0] | (buff[1]<<8);
|
||||
rstVect = vect;
|
||||
wdtVect = buff[8] | (buff[9]<<8);
|
||||
vect -= 4; // Instruction is a relative jump (rjmp), so recalculate.
|
||||
buff[8] = vect & 0xff;
|
||||
buff[9] = vect >> 8;
|
||||
|
||||
// Add jump to bootloader at RESET vector
|
||||
buff[0] = 0x7f;
|
||||
buff[1] = 0xce; // rjmp 0x1d00 instruction
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy buffer into programming buffer
|
||||
bufPtr = buff;
|
||||
addrPtr = (uint16_t)(void*)address;
|
||||
ch = SPM_PAGESIZE / 2;
|
||||
do {
|
||||
uint16_t a;
|
||||
// a = *bufPtr++;
|
||||
// a |= (*bufPtr++) << 8;
|
||||
|
||||
a = *((uint16_t *)bufPtr) ;
|
||||
bufPtr += 2 ;
|
||||
|
||||
__boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
||||
addrPtr += 2;
|
||||
} while (--ch);
|
||||
|
||||
// Write from programming buffer
|
||||
__boot_page_write_short((uint16_t)(void*)address);
|
||||
boot_spm_busy_wait();
|
||||
|
||||
#if defined(RWWSRE)
|
||||
// Reenable read access to flash
|
||||
boot_rww_enable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Read memory block mode, length is big endian. */
|
||||
else if(ch == STK_READ_PAGE)
|
||||
{
|
||||
register uint8_t length;
|
||||
// READ PAGE - we only read flash
|
||||
getch(); /* getlen() */
|
||||
length = getch();
|
||||
getch();
|
||||
|
||||
verifySpace();
|
||||
#ifdef VIRTUAL_BOOT_PARTITION
|
||||
do {
|
||||
// Undo vector patch in bottom page so verify passes
|
||||
if (address == 0) ch=rstVect & 0xff;
|
||||
else if (address == 1) ch=rstVect >> 8;
|
||||
else if (address == 8) ch=wdtVect & 0xff;
|
||||
else if (address == 9) ch=wdtVect >> 8;
|
||||
else ch = pgm_read_byte_near(address);
|
||||
address++;
|
||||
putch(ch);
|
||||
} while (--length);
|
||||
#else
|
||||
#ifdef RAMPZ
|
||||
// Since RAMPZ should already be set, we need to use EPLM directly.
|
||||
// do putch(pgm_read_byte_near(address++));
|
||||
// while (--length);
|
||||
do {
|
||||
uint8_t result;
|
||||
__asm__ ("elpm %0,Z\n":"=r"(result):"z"(address));
|
||||
putch(result);
|
||||
address++;
|
||||
}
|
||||
while (--length);
|
||||
#else
|
||||
do putch(pgm_read_byte_near(address++));
|
||||
while (--length);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get device signature bytes */
|
||||
else if(ch == STK_READ_SIGN)
|
||||
{
|
||||
// READ SIGN - return what Avrdude wants to hear
|
||||
verifySpace();
|
||||
putch(SIGNATURE_0);
|
||||
putch(SIGNATURE_1);
|
||||
putch(SIGNATURE_2);
|
||||
}
|
||||
else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
|
||||
// Adaboot no-wait mod
|
||||
|
||||
// watchdogConfig(WATCHDOG_16MS);
|
||||
|
||||
verifySpace();
|
||||
#ifdef MULTI_CALLED
|
||||
putch(STK_OK);
|
||||
while (!(UART_SRA & _BV(TXC0)));
|
||||
appStart() ;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// This covers the response to commands like STK_ENTER_PROGMODE
|
||||
verifySpace();
|
||||
}
|
||||
putch(STK_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void putch(char ch) {
|
||||
#ifndef SOFT_UART
|
||||
while (!(UART_SRA & _BV(UDRE0)));
|
||||
UART_UDR = ch;
|
||||
#else
|
||||
__asm__ __volatile__ (
|
||||
" com %[ch]\n" // ones complement, carry set
|
||||
" sec\n"
|
||||
"1: brcc 2f\n"
|
||||
" cbi %[uartPort],%[uartBit]\n"
|
||||
" rjmp 3f\n"
|
||||
"2: sbi %[uartPort],%[uartBit]\n"
|
||||
" nop\n"
|
||||
"3: rcall uartDelay\n"
|
||||
" rcall uartDelay\n"
|
||||
" lsr %[ch]\n"
|
||||
" dec %[bitcnt]\n"
|
||||
" brne 1b\n"
|
||||
:
|
||||
:
|
||||
[bitcnt] "d" (10),
|
||||
[ch] "r" (ch),
|
||||
[uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
|
||||
[uartBit] "I" (UART_TX_BIT)
|
||||
:
|
||||
"r25"
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t getch(void) {
|
||||
uint8_t ch;
|
||||
|
||||
#ifdef LED_DATA_FLASH
|
||||
#ifdef __AVR_ATmega8__
|
||||
LED_PORT ^= _BV(LED);
|
||||
#else
|
||||
LED_PIN |= _BV(LED);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SOFT_UART
|
||||
__asm__ __volatile__ (
|
||||
"1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
|
||||
" rjmp 1b\n"
|
||||
" rcall uartDelay\n" // Get to middle of start bit
|
||||
"2: rcall uartDelay\n" // Wait 1 bit period
|
||||
" rcall uartDelay\n" // Wait 1 bit period
|
||||
" clc\n"
|
||||
" sbic %[uartPin],%[uartBit]\n"
|
||||
" sec\n"
|
||||
" dec %[bitCnt]\n"
|
||||
" breq 3f\n"
|
||||
" ror %[ch]\n"
|
||||
" rjmp 2b\n"
|
||||
"3:\n"
|
||||
:
|
||||
[ch] "=r" (ch)
|
||||
:
|
||||
[bitCnt] "d" (9),
|
||||
[uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
|
||||
[uartBit] "I" (UART_RX_BIT)
|
||||
:
|
||||
"r25"
|
||||
);
|
||||
#else
|
||||
while(!(UART_SRA & _BV(RXC0)))
|
||||
|
||||
// watchdogReset()
|
||||
|
||||
;
|
||||
// if (!(UART_SRA & _BV(FE0))) {
|
||||
/*
|
||||
* A Framing Error indicates (probably) that something is talking
|
||||
* to us at the wrong bit rate. Assume that this is because it
|
||||
* expects to be talking to the application, and DON'T reset the
|
||||
* watchdog. This should cause the bootloader to abort and run
|
||||
* the application "soon", if it keeps happening. (Note that we
|
||||
* don't care that an invalid char is returned...)
|
||||
*/
|
||||
// watchdogReset();
|
||||
// }
|
||||
|
||||
ch = UART_UDR;
|
||||
#endif
|
||||
|
||||
#ifdef LED_DATA_FLASH
|
||||
#ifdef __AVR_ATmega8__
|
||||
LED_PORT ^= _BV(LED);
|
||||
#else
|
||||
LED_PIN |= _BV(LED);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
#ifdef SOFT_UART
|
||||
// AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
|
||||
// Adding 3 to numerator simulates nearest rounding for more accurate baud rates
|
||||
#define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6)
|
||||
#if UART_B_VALUE > 255
|
||||
#error Baud rate too slow for soft UART
|
||||
#endif
|
||||
|
||||
void uartDelay() {
|
||||
__asm__ __volatile__ (
|
||||
"ldi r25,%[count]\n"
|
||||
"1:dec r25\n"
|
||||
"brne 1b\n"
|
||||
"ret\n"
|
||||
::[count] "M" (UART_B_VALUE)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
void getNch(uint8_t count) {
|
||||
do getch(); while (--count);
|
||||
verifySpace();
|
||||
}
|
||||
|
||||
void verifySpace()
|
||||
{
|
||||
if ( getch() != CRC_EOP) {
|
||||
|
||||
putch(STK_NOSYNC);
|
||||
// watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
|
||||
//
|
||||
// while (1) // and busy-loop so that WD causes
|
||||
// ; // a reset and app start.
|
||||
}
|
||||
putch(STK_INSYNC);
|
||||
}
|
||||
|
||||
#if LED_START_FLASHES > 0
|
||||
void flash_led(uint8_t count) {
|
||||
do {
|
||||
TCNT1 = -(F_CPU/(1024*16));
|
||||
TIFR1 = _BV(TOV1);
|
||||
while(!(TIFR1 & _BV(TOV1)));
|
||||
//#ifdef __AVR_ATmega8__
|
||||
LED_PORT ^= _BV(LED);
|
||||
//#else
|
||||
// LED_PIN |= _BV(LED);
|
||||
//#endif
|
||||
watchdogReset();
|
||||
} while (--count);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Watchdog functions. These are only safe with interrupts turned off.
|
||||
void watchdogReset() {
|
||||
__asm__ __volatile__ (
|
||||
"wdr\n"
|
||||
);
|
||||
}
|
||||
|
||||
void watchdogConfig(uint8_t x) {
|
||||
WDTCSR = _BV(WDCE) | _BV(WDE);
|
||||
WDTCSR = x;
|
||||
}
|
||||
|
||||
void appStart()
|
||||
{
|
||||
// watchdogConfig(WATCHDOG_OFF);
|
||||
// __asm__ __volatile__ (
|
||||
//#ifdef VIRTUAL_BOOT_PARTITION
|
||||
// // Jump to WDT vector
|
||||
// "ldi r30,4\n"
|
||||
// "clr r31\n"
|
||||
//#else
|
||||
// // Jump to RST vector
|
||||
// "clr r30\n"
|
||||
// "clr r31\n"
|
||||
//#endif
|
||||
// "ijmp\n"
|
||||
// );
|
||||
|
||||
register void (*p)() ;
|
||||
p = 0 ;
|
||||
|
||||
if ( pgm_read_byte( (uint16_t)p ) != 0xFF )
|
||||
{
|
||||
(*p)() ;
|
||||
}
|
||||
}
|
||||
|
||||
80
BootLoaders/AtmegaMultiBoot/Source/pin_defs.h
Normal file
80
BootLoaders/AtmegaMultiBoot/Source/pin_defs.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
||||
/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB5
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTD
|
||||
#define UART_PIN PIND
|
||||
#define UART_DDR DDRD
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__AVR_ATmega8__)
|
||||
//Name conversion R.Wiersma
|
||||
#define UCSR0A UCSRA
|
||||
#define UDR0 UDR
|
||||
#define UDRE0 UDRE
|
||||
#define RXC0 RXC
|
||||
#define FE0 FE
|
||||
#define TIFR1 TIFR
|
||||
#define WDTCSR WDTCR
|
||||
#endif
|
||||
|
||||
/* Luminet support */
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
/* Red LED is connected to pin PA4 */
|
||||
#define LED_DDR DDRA
|
||||
#define LED_PORT PORTA
|
||||
#define LED_PIN PINA
|
||||
#define LED PINA4
|
||||
/* Ports for soft UART - left port only for now. TX/RX on PA2/PA3 */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTA
|
||||
#define UART_PIN PINA
|
||||
#define UART_DDR DDRA
|
||||
#define UART_TX_BIT 2
|
||||
#define UART_RX_BIT 3
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Sanguino support */
|
||||
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
|
||||
/* Onboard LED is connected to pin PB0 on Sanguino */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB0
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTD
|
||||
#define UART_PIN PIND
|
||||
#define UART_DDR DDRD
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Mega support */
|
||||
#if defined(__AVR_ATmega1280__)
|
||||
/* Onboard LED is connected to pin PB7 on Arduino Mega */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB7
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTE
|
||||
#define UART_PIN PINE
|
||||
#define UART_DDR DDRE
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
39
BootLoaders/AtmegaMultiBoot/Source/stk500.h
Normal file
39
BootLoaders/AtmegaMultiBoot/Source/stk500.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* STK500 constants list, from AVRDUDE */
|
||||
#define STK_OK 0x10
|
||||
#define STK_FAILED 0x11 // Not used
|
||||
#define STK_UNKNOWN 0x12 // Not used
|
||||
#define STK_NODEVICE 0x13 // Not used
|
||||
#define STK_INSYNC 0x14 // ' '
|
||||
#define STK_NOSYNC 0x15 // Not used
|
||||
#define ADC_CHANNEL_ERROR 0x16 // Not used
|
||||
#define ADC_MEASURE_OK 0x17 // Not used
|
||||
#define PWM_CHANNEL_ERROR 0x18 // Not used
|
||||
#define PWM_ADJUST_OK 0x19 // Not used
|
||||
#define CRC_EOP 0x20 // 'SPACE'
|
||||
#define STK_GET_SYNC 0x30 // '0'
|
||||
#define STK_GET_SIGN_ON 0x31 // '1'
|
||||
#define STK_SET_PARAMETER 0x40 // '@'
|
||||
#define STK_GET_PARAMETER 0x41 // 'A'
|
||||
#define STK_SET_DEVICE 0x42 // 'B'
|
||||
#define STK_SET_DEVICE_EXT 0x45 // 'E'
|
||||
#define STK_ENTER_PROGMODE 0x50 // 'P'
|
||||
#define STK_LEAVE_PROGMODE 0x51 // 'Q'
|
||||
#define STK_CHIP_ERASE 0x52 // 'R'
|
||||
#define STK_CHECK_AUTOINC 0x53 // 'S'
|
||||
#define STK_LOAD_ADDRESS 0x55 // 'U'
|
||||
#define STK_UNIVERSAL 0x56 // 'V'
|
||||
#define STK_PROG_FLASH 0x60 // '`'
|
||||
#define STK_PROG_DATA 0x61 // 'a'
|
||||
#define STK_PROG_FUSE 0x62 // 'b'
|
||||
#define STK_PROG_LOCK 0x63 // 'c'
|
||||
#define STK_PROG_PAGE 0x64 // 'd'
|
||||
#define STK_PROG_FUSE_EXT 0x65 // 'e'
|
||||
#define STK_READ_FLASH 0x70 // 'p'
|
||||
#define STK_READ_DATA 0x71 // 'q'
|
||||
#define STK_READ_FUSE 0x72 // 'r'
|
||||
#define STK_READ_LOCK 0x73 // 's'
|
||||
#define STK_READ_PAGE 0x74 // 't'
|
||||
#define STK_READ_SIGN 0x75 // 'u'
|
||||
#define STK_READ_OSCCAL 0x76 // 'v'
|
||||
#define STK_READ_FUSE_EXT 0x77 // 'w'
|
||||
#define STK_READ_OSCCAL_EXT 0x78 // 'x'
|
||||
5
BootLoaders/Boards/Linux/45-maple.rules
Normal file
5
BootLoaders/Boards/Linux/45-maple.rules
Normal file
@@ -0,0 +1,5 @@
|
||||
ATTRS{idProduct}=="1001", ATTRS{idVendor}=="0110", MODE="664", GROUP="plugdev"
|
||||
ATTRS{idProduct}=="1002", ATTRS{idVendor}=="0110", MODE="664", GROUP="plugdev"
|
||||
ATTRS{idProduct}=="0003", ATTRS{idVendor}=="1eaf", MODE="664", GROUP="plugdev" SYMLINK+="maple", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
ATTRS{idProduct}=="0004", ATTRS{idVendor}=="1eaf", MODE="664", GROUP="plugdev" SYMLINK+="maple", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
|
||||
11
BootLoaders/Boards/Windows/install_drivers.bat
Normal file
11
BootLoaders/Boards/Windows/install_drivers.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
|
||||
echo Installing MULTI-Module DFU Bootloader Driver...
|
||||
"%~dp0wdi-simple" --vid 0x1EAF --pid 0x0003 --type 2 --name "MULTI-Module DFU Bootloader" --dest "%~dp0MULTI-DFU-Bootloader" -b
|
||||
echo.
|
||||
|
||||
echo Installing MULTI-Module USB Serial Driver...
|
||||
"%~dp0wdi-simple" --vid 0x1EAF --pid 0x0004 --type 3 --name "MULTI-Module USB Serial" --dest "%~dp0MULTI-USB-Serial" -b
|
||||
echo.
|
||||
|
||||
pause
|
||||
BIN
BootLoaders/Boards/Windows/wdi-simple.exe
Normal file
BIN
BootLoaders/Boards/Windows/wdi-simple.exe
Normal file
Binary file not shown.
47
BootLoaders/OrangeMultiBoot/OrangeMultiBoot.hex
Normal file
47
BootLoaders/OrangeMultiBoot/OrangeMultiBoot.hex
Normal file
@@ -0,0 +1,47 @@
|
||||
:108000001F92CDB7DEB7CFD01124809178009FEFBB
|
||||
:1080100090937800837099F088EA91E680936808DD
|
||||
:108020009093690880E180934C0880914C0884FF0C
|
||||
:10803000FCCF109240088091680682FD8FD082E0CC
|
||||
:1080400080936106C12CD12C97D0813479F494D0DF
|
||||
:10805000898399D08981823811F485E005C08138FF
|
||||
:1080600011F484E001C083E080D075C0823411F443
|
||||
:1080700084E103C0853419F485E08CD06CC085356B
|
||||
:1080800059F47AD0C82E78D0D12CD82A8D2D881FBB
|
||||
:108090008827881F8BBF5EC0863521F484E07AD0A4
|
||||
:1080A00080E0E2CF843641F567D066D0F82E64D008
|
||||
:1080B000C601DCD000E010E25FD0F80181938F01AF
|
||||
:1080C000FE12FACF60D0D7FC46C0CBD0C601DAD0C2
|
||||
:1080D000760100E010E2F801619171918F01C70112
|
||||
:1080E000DBD0F2E0EF0EF11C011581E2180799F7E1
|
||||
:1080F000C601E0D0B6D02FC08437C1F43DD03CD00B
|
||||
:10810000F82E3AD040D0F601EC2CEF0C8F010F5F27
|
||||
:108110001F4F84912AD0E01207C0EFEFCE1ADE0A7B
|
||||
:10812000FA94CF0CD11C17C0F801F0CF853739F481
|
||||
:108130002AD08EE11AD085E918D082E495CF813516
|
||||
:1081400049F421D080E111D08091A10886FFFCCFB5
|
||||
:1081500005D001C018D080E108D076CFE0E0F0E093
|
||||
:1081600084918F3F09F0099408959091A10895FF9B
|
||||
:10817000FCCF8093A00808958091A10887FFFCCFD1
|
||||
:108180008091A0080895F8DF803211F085E1EDDFDD
|
||||
:1081900084E1EBCFCF93C82FEFDFC150E9F7CF9148
|
||||
:1081A000F2CFA895089583EC8093520080915000FF
|
||||
:1081B0008860809350008091510083FFFCCF82EC57
|
||||
:1081C0008093550080915000806180935000809191
|
||||
:1081D000510084FFFCCF88ED84BF1092400084BF23
|
||||
:1081E00024E02093400087E08093A20087E88093FA
|
||||
:1081F0008301109241081092420810924308109295
|
||||
:10820000440810924608109247088FEF9FEF809322
|
||||
:1082100066089093670810926008109261088BE0DE
|
||||
:1082200080934008209365062093620688E180933E
|
||||
:10823000720698E0909345069093410692E29093DF
|
||||
:10824000A6081092A7088093A4088091A3088F7CA9
|
||||
:1082500080618093A30883E08093A5088091A008A3
|
||||
:1082600008958091CF0187FDFCCF08958F939F9350
|
||||
:1082700082E2E0ECF1E08287FF91EF918DE984BF2B
|
||||
:10828000E8950895FC0186E28093CA0188ED84BFD9
|
||||
:1082900081E08093CB0108950F921F92FC01062E7E
|
||||
:1082A000172E83E28093CA018DE984BFE8951F9061
|
||||
:1082B0000F900895FC018EE28093CA018DE984BF7E
|
||||
:0482C000E8950895A0
|
||||
:040000030000800079
|
||||
:00000001FF
|
||||
503
BootLoaders/OrangeMultiBoot/Source/Makefile
Normal file
503
BootLoaders/OrangeMultiBoot/Source/Makefile
Normal file
@@ -0,0 +1,503 @@
|
||||
# Makefile for ATmegaBOOT
|
||||
# E.Lins, 18.7.2005
|
||||
# $Id$
|
||||
#
|
||||
# Instructions
|
||||
#
|
||||
# To make bootloader .hex file:
|
||||
# make diecimila
|
||||
# make lilypad
|
||||
# make ng
|
||||
# etc...
|
||||
#
|
||||
# To burn bootloader .hex file:
|
||||
# make diecimila_isp
|
||||
# make lilypad_isp
|
||||
# make ng_isp
|
||||
# etc...
|
||||
|
||||
# program name should not be changed...
|
||||
PROGRAM = optiboot
|
||||
|
||||
# The default behavior is to build using tools that are in the users
|
||||
# current path variables, but we can also build using an installed
|
||||
# Arduino user IDE setup, or the Arduino source tree.
|
||||
# Uncomment this next lines to build within the arduino environment,
|
||||
# using the arduino-included avrgcc toolset (mac and pc)
|
||||
# ENV ?= arduino
|
||||
# ENV ?= arduinodev
|
||||
# OS ?= macosx
|
||||
# OS ?= windows
|
||||
|
||||
|
||||
# enter the parameters for the avrdude isp tool -b19200
|
||||
#
|
||||
# These are the parameters for a usb-based STK500v2 programmer.
|
||||
# Exact type unknown. (historical Makefile values.)
|
||||
ISPTOOL = stk500v2
|
||||
ISPPORT = usb
|
||||
ISPSPEED = -b 57600
|
||||
#
|
||||
#
|
||||
# These are parameters for using an Arduino with the ArduinoISP sketch
|
||||
# as the programmer. On a mac, for a particular Uno as programmer.
|
||||
#ISPTOOL = stk500v1 -C /Applications/arduino/arduino-0022/hardware/tools/avr/etc/avrdude.conf
|
||||
#ISPPORT = /dev/tty.usbmodemfd3141
|
||||
#ISPSPEED = -b19200
|
||||
|
||||
MCU_TARGET = atmega168
|
||||
LDSECTIONS = -Wl,--section-start=.text=0x3e00 -Wl,--section-start=.version=0x3ffe
|
||||
|
||||
# Build environments
|
||||
# Start of some ugly makefile-isms to allow optiboot to be built
|
||||
# in several different environments. See the README.TXT file for
|
||||
# details.
|
||||
|
||||
# default
|
||||
fixpath = $(1)
|
||||
|
||||
ifeq ($(ENV), arduino)
|
||||
# For Arduino, we assume that we're connected to the optiboot directory
|
||||
# included with the arduino distribution, which means that the full set
|
||||
# of avr-tools are "right up there" in standard places.
|
||||
TOOLROOT = ../../../tools
|
||||
GCCROOT = $(TOOLROOT)/avr/bin/
|
||||
AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf
|
||||
|
||||
ifeq ($(OS), windows)
|
||||
# On windows, SOME of the tool paths will need to have backslashes instead
|
||||
# of forward slashes (because they use windows cmd.exe for execution instead
|
||||
# of a unix/mingw shell?) We also have to ensure that a consistent shell
|
||||
# is used even if a unix shell is installed (ie as part of WINAVR)
|
||||
fixpath = $(subst /,\,$1)
|
||||
SHELL = cmd.exe
|
||||
endif
|
||||
|
||||
else ifeq ($(ENV), arduinodev)
|
||||
# Arduino IDE source code environment. Use the unpacked compilers created
|
||||
# by the build (you'll need to do "ant build" first.)
|
||||
ifeq ($(OS), macosx)
|
||||
TOOLROOT = ../../../../build/macosx/work/Arduino.app/Contents/Resources/Java/hardware/tools
|
||||
endif
|
||||
ifeq ($(OS), windows)
|
||||
TOOLROOT = ../../../../build/windows/work/hardware/tools
|
||||
endif
|
||||
|
||||
GCCROOT = $(TOOLROOT)/avr/bin/
|
||||
AVRDUDE_CONF = -C$(TOOLROOT)/avr/etc/avrdude.conf
|
||||
|
||||
else
|
||||
GCCROOT =
|
||||
AVRDUDE_CONF =
|
||||
endif
|
||||
#
|
||||
# End of build environment code.
|
||||
|
||||
|
||||
# the efuse should really be 0xf8; since, however, only the lower
|
||||
# three bits of that byte are used on the atmega168, avrdude gets
|
||||
# confused if you specify 1's for the higher bits, see:
|
||||
# http://tinker.it/now/2007/02/24/the-tale-of-avrdude-atmega168-and-extended-bits-fuses/
|
||||
#
|
||||
# similarly, the lock bits should be 0xff instead of 0x3f (to
|
||||
# unlock the bootloader section) and 0xcf instead of 0x2f (to
|
||||
# lock it), but since the high two bits of the lock byte are
|
||||
# unused, avrdude would get confused.
|
||||
|
||||
ISPFUSES = $(GCCROOT)avrdude $(AVRDUDE_CONF) -c $(ISPTOOL) \
|
||||
-p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \
|
||||
-e -u -U lock:w:0x3f:m -U efuse:w:0x$(EFUSE):m \
|
||||
-U hfuse:w:0x$(HFUSE):m -U lfuse:w:0x$(LFUSE):m
|
||||
ISPFLASH = $(GCCROOT)avrdude $(AVRDUDE_CONF) -c $(ISPTOOL) \
|
||||
-p $(MCU_TARGET) -P $(ISPPORT) $(ISPSPEED) \
|
||||
-U flash:w:$(PROGRAM)_$(TARGET).hex -U lock:w:0x2f:m
|
||||
|
||||
STK500 = "C:\Program Files\Atmel\AVR Tools\STK500\Stk500.exe"
|
||||
STK500-1 = $(STK500) -e -d$(MCU_TARGET) -pf -vf -if$(PROGRAM)_$(TARGET).hex \
|
||||
-lFF -LFF -f$(HFUSE)$(LFUSE) -EF8 -ms -q -cUSB -I200kHz -s -wt
|
||||
STK500-2 = $(STK500) -d$(MCU_TARGET) -ms -q -lCF -LCF -cUSB -I200kHz -s -wt
|
||||
|
||||
OBJ = $(PROGRAM).o
|
||||
OPTIMIZE = -Os -fno-inline-small-functions -fno-split-wide-types
|
||||
# -mshort-calls
|
||||
|
||||
DEFS =
|
||||
LIBS =
|
||||
|
||||
CC = $(GCCROOT)avr-gcc
|
||||
|
||||
# Override is only needed by avr-lib build system.
|
||||
|
||||
override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) -DF_CPU=$(AVR_FREQ) $(DEFS)
|
||||
override LDFLAGS = $(LDSECTIONS) -Wl,--relax -Wl,--gc-sections -nostartfiles -nostdlib
|
||||
|
||||
OBJCOPY = $(GCCROOT)avr-objcopy
|
||||
OBJDUMP = $(call fixpath,$(GCCROOT)avr-objdump)
|
||||
|
||||
SIZE = $(GCCROOT)avr-size
|
||||
|
||||
|
||||
|
||||
#Voice board test
|
||||
# ATmega328
|
||||
#
|
||||
#atmega328: TARGET = atmega328p
|
||||
#atmega328: MCU_TARGET = atmega328p
|
||||
#atmega328: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
#atmega328: AVR_FREQ = 12000000L
|
||||
#atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
#atmega328: $(PROGRAM)_atmega328.hex
|
||||
#atmega328: $(PROGRAM)_atmega328.lst
|
||||
|
||||
xmega32D4: TARGET = atxmega32d4
|
||||
xmega32D4: MCU_TARGET = atxmega32d4
|
||||
xmega32D4: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=57600'
|
||||
xmega32D4: AVR_FREQ = 32000000L
|
||||
xmega32D4: LDSECTIONS = -Wl,--section-start=.text=0x8000
|
||||
xmega32D4: $(PROGRAM)_xmega32d4.hex
|
||||
xmega32D4: $(PROGRAM)_xmega32d4.lst
|
||||
|
||||
atmega328: TARGET = atmega328
|
||||
atmega328: MCU_TARGET = atmega328p
|
||||
atmega328: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=57600'
|
||||
atmega328: AVR_FREQ = 16000000L
|
||||
atmega328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
atmega328: $(PROGRAM)_atmega328_16.hex
|
||||
atmega328: $(PROGRAM)_atmega328_16.lst
|
||||
|
||||
|
||||
# Test platforms
|
||||
# Virtual boot block test
|
||||
virboot328: TARGET = atmega328
|
||||
virboot328: MCU_TARGET = atmega328p
|
||||
virboot328: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DVIRTUAL_BOOT'
|
||||
virboot328: AVR_FREQ = 16000000L
|
||||
virboot328: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
virboot328: $(PROGRAM)_atmega328.hex
|
||||
virboot328: $(PROGRAM)_atmega328.lst
|
||||
|
||||
# 20MHz clocked platforms
|
||||
#
|
||||
# These are capable of 230400 baud, or 38400 baud on PC (Arduino Avrdude issue)
|
||||
#
|
||||
|
||||
pro20: TARGET = pro_20mhz
|
||||
pro20: MCU_TARGET = atmega168
|
||||
pro20: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro20: AVR_FREQ = 20000000L
|
||||
pro20: $(PROGRAM)_pro_20mhz.hex
|
||||
pro20: $(PROGRAM)_pro_20mhz.lst
|
||||
|
||||
pro20_isp: pro20
|
||||
pro20_isp: TARGET = pro_20mhz
|
||||
# 2.7V brownout
|
||||
pro20_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro20_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro20_isp: EFUSE = 04
|
||||
pro20_isp: isp
|
||||
|
||||
# 16MHz clocked platforms
|
||||
#
|
||||
# These are capable of 230400 baud, or 38400 baud on PC (Arduino Avrdude issue)
|
||||
#
|
||||
|
||||
pro16: TARGET = pro_16MHz
|
||||
pro16: MCU_TARGET = atmega168
|
||||
pro16: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro16: AVR_FREQ = 16000000L
|
||||
pro16: $(PROGRAM)_pro_16MHz.hex
|
||||
pro16: $(PROGRAM)_pro_16MHz.lst
|
||||
|
||||
pro16_isp: pro16
|
||||
pro16_isp: TARGET = pro_16MHz
|
||||
# 2.7V brownout
|
||||
pro16_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro16_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro16_isp: EFUSE = 04
|
||||
pro16_isp: isp
|
||||
|
||||
# Diecimila, Duemilanove with m168, and NG use identical bootloaders
|
||||
# Call it "atmega168" for generality and clarity, keep "diecimila" for
|
||||
# backward compatibility of makefile
|
||||
#
|
||||
atmega168: TARGET = atmega168
|
||||
atmega168: MCU_TARGET = atmega168
|
||||
atmega168: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
atmega168: AVR_FREQ = 12000000L
|
||||
atmega168: $(PROGRAM)_atmega168.hex
|
||||
atmega168: $(PROGRAM)_atmega168.lst
|
||||
|
||||
atmega168_isp: atmega168
|
||||
atmega168_isp: TARGET = atmega168
|
||||
# 2.7V brownout
|
||||
atmega168_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega168_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
atmega168_isp: EFUSE = 04
|
||||
atmega168_isp: isp
|
||||
|
||||
diecimila: TARGET = diecimila
|
||||
diecimila: MCU_TARGET = atmega168
|
||||
diecimila: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
diecimila: AVR_FREQ = 16000000L
|
||||
diecimila: $(PROGRAM)_diecimila.hex
|
||||
diecimila: $(PROGRAM)_diecimila.lst
|
||||
|
||||
diecimila_isp: diecimila
|
||||
diecimila_isp: TARGET = diecimila
|
||||
# 2.7V brownout
|
||||
diecimila_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
diecimila_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
diecimila_isp: EFUSE = 04
|
||||
diecimila_isp: isp
|
||||
|
||||
atmega328_isp: atmega328
|
||||
atmega328_isp: TARGET = atmega328
|
||||
atmega328_isp: MCU_TARGET = atmega328p
|
||||
# 512 byte boot, SPIEN
|
||||
atmega328_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega328_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega328_isp: EFUSE = FD
|
||||
atmega328_isp: isp
|
||||
|
||||
atmega1284: TARGET = atmega1284p
|
||||
atmega1284: MCU_TARGET = atmega1284p
|
||||
atmega1284: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
atmega1284: AVR_FREQ = 16000000L
|
||||
atmega1284: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe
|
||||
atmega1284: $(PROGRAM)_atmega1284p.hex
|
||||
atmega1284: $(PROGRAM)_atmega1284p.lst
|
||||
|
||||
atmega1284_isp: atmega1284
|
||||
atmega1284_isp: TARGET = atmega1284p
|
||||
atmega1284_isp: MCU_TARGET = atmega1284p
|
||||
# 1024 byte boot
|
||||
atmega1284_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega1284_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega1284_isp: EFUSE = FD
|
||||
atmega1284_isp: isp
|
||||
|
||||
# Sanguino has a minimum boot size of 1024 bytes, so enable extra functions
|
||||
#
|
||||
sanguino: TARGET = atmega644p
|
||||
sanguino: MCU_TARGET = atmega644p
|
||||
sanguino: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
sanguino: AVR_FREQ = 16000000L
|
||||
sanguino: LDSECTIONS = -Wl,--section-start=.text=0xfc00 -Wl,--section-start=.version=0xfffe
|
||||
sanguino: $(PROGRAM)_atmega644p.hex
|
||||
sanguino: $(PROGRAM)_atmega644p.lst
|
||||
|
||||
sanguino_isp: sanguino
|
||||
sanguino_isp: TARGET = atmega644p
|
||||
sanguino_isp: MCU_TARGET = atmega644p
|
||||
# 1024 byte boot
|
||||
sanguino_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
sanguino_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
sanguino_isp: EFUSE = FD
|
||||
sanguino_isp: isp
|
||||
|
||||
# Mega has a minimum boot size of 1024 bytes, so enable extra functions
|
||||
#mega: TARGET = atmega1280
|
||||
mega1280: MCU_TARGET = atmega1280
|
||||
mega1280: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400' '-DBIGBOOT'
|
||||
mega1280: AVR_FREQ = 16000000L
|
||||
mega1280: LDSECTIONS = -Wl,--section-start=.text=0x1fc00 -Wl,--section-start=.version=0x1fffe
|
||||
mega1280: $(PROGRAM)_atmega1280.hex
|
||||
mega1280: $(PROGRAM)_atmega1280.lst
|
||||
|
||||
mega1280_isp: mega
|
||||
mega1280_isp: TARGET = atmega1280
|
||||
mega1280_isp: MCU_TARGET = atmega1280
|
||||
# 1024 byte boot
|
||||
mega1280_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
mega1280_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
mega1280_isp: EFUSE = FD
|
||||
mega1280_isp: isp
|
||||
|
||||
# ATmega8
|
||||
#
|
||||
atmega8: TARGET = atmega8
|
||||
atmega8: MCU_TARGET = atmega8
|
||||
atmega8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
atmega8: AVR_FREQ = 16000000L
|
||||
atmega8: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe
|
||||
atmega8: $(PROGRAM)_atmega8.hex
|
||||
atmega8: $(PROGRAM)_atmega8.lst
|
||||
|
||||
atmega8_isp: atmega8
|
||||
atmega8_isp: TARGET = atmega8
|
||||
atmega8_isp: MCU_TARGET = atmega8
|
||||
# SPIEN, CKOPT, Bootsize=512B
|
||||
atmega8_isp: HFUSE = CC
|
||||
# 2.7V brownout, Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega8_isp: LFUSE = BF
|
||||
atmega8_isp: isp
|
||||
|
||||
# ATmega88
|
||||
#
|
||||
atmega88: TARGET = atmega88
|
||||
atmega88: MCU_TARGET = atmega88
|
||||
atmega88: CFLAGS += '-DLED_START_FLASHES=0' '-DBAUD_RATE=38400'
|
||||
atmega88: AVR_FREQ = 12000000L
|
||||
atmega88: LDSECTIONS = -Wl,--section-start=.text=0x1e00 -Wl,--section-start=.version=0x1ffe
|
||||
atmega88: $(PROGRAM)_atmega88.hex
|
||||
atmega88: $(PROGRAM)_atmega88.lst
|
||||
|
||||
atmega88_isp: atmega88
|
||||
atmega88_isp: TARGET = atmega88
|
||||
atmega88_isp: MCU_TARGET = atmega88
|
||||
# 2.7V brownout
|
||||
atmega88_isp: HFUSE = DD
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atemga88_isp: LFUSE = FF
|
||||
# 512 byte boot
|
||||
atmega88_isp: EFUSE = 04
|
||||
atmega88_isp: isp
|
||||
|
||||
|
||||
# 8MHz clocked platforms
|
||||
#
|
||||
# These are capable of 38400 baud
|
||||
#
|
||||
|
||||
lilypad: TARGET = lilypad
|
||||
lilypad: MCU_TARGET = atmega168
|
||||
lilypad: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
lilypad: AVR_FREQ = 8000000L
|
||||
lilypad: $(PROGRAM)_lilypad.hex
|
||||
lilypad: $(PROGRAM)_lilypad.lst
|
||||
|
||||
lilypad_isp: lilypad
|
||||
lilypad_isp: TARGET = lilypad
|
||||
# 2.7V brownout
|
||||
lilypad_isp: HFUSE = DD
|
||||
# Internal 8MHz osc (8MHz) Slow rising power
|
||||
lilypad_isp: LFUSE = E2
|
||||
# 512 byte boot
|
||||
lilypad_isp: EFUSE = 04
|
||||
lilypad_isp: isp
|
||||
|
||||
lilypad_resonator: TARGET = lilypad_resonator
|
||||
lilypad_resonator: MCU_TARGET = atmega168
|
||||
lilypad_resonator: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
lilypad_resonator: AVR_FREQ = 8000000L
|
||||
lilypad_resonator: $(PROGRAM)_lilypad_resonator.hex
|
||||
lilypad_resonator: $(PROGRAM)_lilypad_resonator.lst
|
||||
|
||||
lilypad_resonator_isp: lilypad_resonator
|
||||
lilypad_resonator_isp: TARGET = lilypad_resonator
|
||||
# 2.7V brownout
|
||||
lilypad_resonator_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
lilypad_resonator_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
lilypad_resonator_isp: EFUSE = 04
|
||||
lilypad_resonator_isp: isp
|
||||
|
||||
pro8: TARGET = pro_8MHz
|
||||
pro8: MCU_TARGET = atmega168
|
||||
pro8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
pro8: AVR_FREQ = 8000000L
|
||||
pro8: $(PROGRAM)_pro_8MHz.hex
|
||||
pro8: $(PROGRAM)_pro_8MHz.lst
|
||||
|
||||
pro8_isp: pro8
|
||||
pro8_isp: TARGET = pro_8MHz
|
||||
# 2.7V brownout
|
||||
pro8_isp: HFUSE = DD
|
||||
# Full swing xtal (20MHz) 258CK/14CK+4.1ms
|
||||
pro8_isp: LFUSE = C6
|
||||
# 512 byte boot
|
||||
pro8_isp: EFUSE = 04
|
||||
pro8_isp: isp
|
||||
|
||||
atmega328_pro8: TARGET = atmega328_pro_8MHz
|
||||
atmega328_pro8: MCU_TARGET = atmega328p
|
||||
atmega328_pro8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=38400'
|
||||
atmega328_pro8: AVR_FREQ = 8000000L
|
||||
atmega328_pro8: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
|
||||
atmega328_pro8: $(PROGRAM)_atmega328_pro_8MHz.hex
|
||||
atmega328_pro8: $(PROGRAM)_atmega328_pro_8MHz.lst
|
||||
|
||||
atmega328_pro8_isp: atmega328_pro8
|
||||
atmega328_pro8_isp: TARGET = atmega328_pro_8MHz
|
||||
atmega328_pro8_isp: MCU_TARGET = atmega328p
|
||||
# 512 byte boot, SPIEN
|
||||
atmega328_pro8_isp: HFUSE = DE
|
||||
# Low power xtal (16MHz) 16KCK/14CK+65ms
|
||||
atmega328_pro8_isp: LFUSE = FF
|
||||
# 2.7V brownout
|
||||
atmega328_pro8_isp: EFUSE = DE
|
||||
atmega328_pro8_isp: isp
|
||||
|
||||
# 1MHz clocked platforms
|
||||
#
|
||||
# These are capable of 9600 baud
|
||||
#
|
||||
|
||||
luminet: TARGET = luminet
|
||||
luminet: MCU_TARGET = attiny84
|
||||
luminet: CFLAGS += '-DLED_START_FLASHES=3' '-DSOFT_UART' '-DBAUD_RATE=9600'
|
||||
luminet: CFLAGS += '-DVIRTUAL_BOOT_PARTITION'
|
||||
luminet: AVR_FREQ = 1000000L
|
||||
luminet: LDSECTIONS = -Wl,--section-start=.text=0x1d00 -Wl,--section-start=.version=0x1efe
|
||||
luminet: $(PROGRAM)_luminet.hex
|
||||
luminet: $(PROGRAM)_luminet.lst
|
||||
|
||||
luminet_isp: luminet
|
||||
luminet_isp: TARGET = luminet
|
||||
luminet_isp: MCU_TARGET = attiny84
|
||||
# Brownout disabled
|
||||
luminet_isp: HFUSE = DF
|
||||
# 1MHz internal oscillator, slowly rising power
|
||||
luminet_isp: LFUSE = 62
|
||||
# Self-programming enable
|
||||
luminet_isp: EFUSE = FE
|
||||
luminet_isp: isp
|
||||
|
||||
#
|
||||
# Generic build instructions
|
||||
#
|
||||
#
|
||||
|
||||
isp: $(TARGET)
|
||||
$(ISPFUSES)
|
||||
$(ISPFLASH)
|
||||
|
||||
isp-stk500: $(PROGRAM)_$(TARGET).hex
|
||||
$(STK500-1)
|
||||
$(STK500-2)
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(SIZE) $@
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.elf *.lst *.map *.sym *.lss *.eep *.srec *.bin *.hex
|
||||
|
||||
%.lst: %.elf
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
%.hex: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex $< $@
|
||||
|
||||
%.srec: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O srec $< $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -j .text -j .data -j .version --set-section-flags .version=alloc,load -O binary $< $@
|
||||
980
BootLoaders/OrangeMultiBoot/Source/optiboot.c
Normal file
980
BootLoaders/OrangeMultiBoot/Source/optiboot.c
Normal file
@@ -0,0 +1,980 @@
|
||||
/**********************************************************/
|
||||
/* Optiboot bootloader for Xmega */
|
||||
/* */
|
||||
/* http://optiboot.googlecode.com */
|
||||
/* */
|
||||
/* Arduino-maintained version : See README.TXT */
|
||||
/* http://code.google.com/p/arduino/ */
|
||||
/* It is the intent that changes not relevant to the */
|
||||
/* Arduino production envionment get moved from the */
|
||||
/* optiboot project to the arduino project in "lumps." */
|
||||
/* */
|
||||
/* Heavily optimised bootloader that is faster and */
|
||||
/* smaller than the Arduino standard bootloader */
|
||||
/* */
|
||||
/* Enhancements: */
|
||||
/* Fits in 512 bytes, saving 1.5K of code space */
|
||||
/* Background page erasing speeds up programming */
|
||||
/* Higher baud rate speeds up programming */
|
||||
/* Written almost entirely in C */
|
||||
/* Customisable timeout with accurate timeconstant */
|
||||
/* Optional virtual UART. No hardware UART required. */
|
||||
/* Optional virtual boot partition for devices without. */
|
||||
/* */
|
||||
/* What you lose: */
|
||||
/* Implements a skeleton STK500 protocol which is */
|
||||
/* missing several features including EEPROM */
|
||||
/* programming and non-page-aligned writes */
|
||||
/* High baud rate breaks compatibility with standard */
|
||||
/* Arduino flash settings */
|
||||
/* */
|
||||
/* Fully supported: */
|
||||
/* ATmega168 based devices (Diecimila etc) */
|
||||
/* ATmega328P based devices (Duemilanove etc) */
|
||||
/* */
|
||||
/* Beta test (believed working.) */
|
||||
/* ATmega8 based devices (Arduino legacy) */
|
||||
/* ATmega328 non-picopower devices */
|
||||
/* ATmega644P based devices (Sanguino) */
|
||||
/* ATmega1284P based devices */
|
||||
/* */
|
||||
/* Alpha test */
|
||||
/* ATmega1280 based devices (Arduino Mega) */
|
||||
/* */
|
||||
/* Work in progress: */
|
||||
/* ATtiny84 based devices (Luminet) */
|
||||
/* */
|
||||
/* Does not support: */
|
||||
/* USB based devices (eg. Teensy) */
|
||||
/* */
|
||||
/* Assumptions: */
|
||||
/* The code makes several assumptions that reduce the */
|
||||
/* code size. They are all true after a hardware reset, */
|
||||
/* but may not be true if the bootloader is called by */
|
||||
/* other means or on other hardware. */
|
||||
/* No interrupts can occur */
|
||||
/* UART and Timer 1 are set to their reset state */
|
||||
/* SP points to RAMEND */
|
||||
/* */
|
||||
/* Code builds on code, libraries and optimisations from: */
|
||||
/* stk500boot.c by Jason P. Kyle */
|
||||
/* Arduino bootloader http://arduino.cc */
|
||||
/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
|
||||
/* avr-libc project http://nongnu.org/avr-libc */
|
||||
/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
|
||||
/* AVR305 Atmel Application Note */
|
||||
/* */
|
||||
/* This program is free software; you can redistribute it */
|
||||
/* and/or modify it under the terms of the GNU General */
|
||||
/* Public License as published by the Free Software */
|
||||
/* Foundation; either version 2 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will */
|
||||
/* be useful, but WITHOUT ANY WARRANTY; without even the */
|
||||
/* implied warranty of MERCHANTABILITY or FITNESS FOR A */
|
||||
/* PARTICULAR PURPOSE. See the GNU General Public */
|
||||
/* License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General */
|
||||
/* Public License along with this program; if not, write */
|
||||
/* to the Free Software Foundation, Inc., */
|
||||
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
/* */
|
||||
/* Licence can be viewed at */
|
||||
/* http://www.fsf.org/licenses/gpl.txt */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
|
||||
/**********************************************************/
|
||||
/* */
|
||||
/* Optional defines: */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
/* */
|
||||
/* BIG_BOOT: */
|
||||
/* Build a 1k bootloader, not 512 bytes. This turns on */
|
||||
/* extra functionality. */
|
||||
/* */
|
||||
/* BAUD_RATE: */
|
||||
/* Set bootloader baud rate. */
|
||||
/* */
|
||||
/* LUDICROUS_SPEED: */
|
||||
/* 230400 baud :-) */
|
||||
/* */
|
||||
/* SOFT_UART: */
|
||||
/* Use AVR305 soft-UART instead of hardware UART. */
|
||||
/* */
|
||||
/* LED_START_FLASHES: */
|
||||
/* Number of LED flashes on bootup. */
|
||||
/* */
|
||||
/* LED_DATA_FLASH: */
|
||||
/* Flash LED when transferring data. For boards without */
|
||||
/* TX or RX LEDs, or for people who like blinky lights. */
|
||||
/* */
|
||||
/* SUPPORT_EEPROM: */
|
||||
/* Support reading and writing from EEPROM. This is not */
|
||||
/* used by Arduino, so off by default. */
|
||||
/* */
|
||||
/* TIMEOUT_MS: */
|
||||
/* Bootloader timeout period, in milliseconds. */
|
||||
/* 500,1000,2000,4000,8000 supported. */
|
||||
/* */
|
||||
/* UART: */
|
||||
/* UART number (0..n) for devices with more than */
|
||||
/* one hardware uart (644P, 1284P, etc) */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
/**********************************************************/
|
||||
/* Version Numbers! */
|
||||
/* */
|
||||
/* Arduino Optiboot now includes this Version number in */
|
||||
/* the source and object code. */
|
||||
/* */
|
||||
/* Version 3 was released as zip from the optiboot */
|
||||
/* repository and was distributed with Arduino 0022. */
|
||||
/* Version 4 starts with the arduino repository commit */
|
||||
/* that brought the arduino repository up-to-date with */
|
||||
/* the optiboot source tree changes since v3. */
|
||||
/* */
|
||||
/**********************************************************/
|
||||
|
||||
/**********************************************************/
|
||||
/* Edit History: */
|
||||
/* */
|
||||
/* Nov 2012 */
|
||||
/* Specific version for 9x voice module */
|
||||
/* by Mike Blandford */
|
||||
/* Mar 2012 */
|
||||
/* 4.5 WestfW: add infrastructure for non-zero UARTS. */
|
||||
/* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */
|
||||
/* Jan 2012: */
|
||||
/* 4.5 WestfW: fix NRWW value for m1284. */
|
||||
/* 4.4 WestfW: use attribute OS_main instead of naked for */
|
||||
/* main(). This allows optimizations that we */
|
||||
/* count on, which are prohibited in naked */
|
||||
/* functions due to PR42240. (keeps us less */
|
||||
/* than 512 bytes when compiler is gcc4.5 */
|
||||
/* (code from 4.3.2 remains the same.) */
|
||||
/* 4.4 WestfW and Maniacbug: Add m1284 support. This */
|
||||
/* does not change the 328 binary, so the */
|
||||
/* version number didn't change either. (?) */
|
||||
/* June 2011: */
|
||||
/* 4.4 WestfW: remove automatic soft_uart detect (didn't */
|
||||
/* know what it was doing or why.) Added a */
|
||||
/* check of the calculated BRG value instead. */
|
||||
/* Version stays 4.4; existing binaries are */
|
||||
/* not changed. */
|
||||
/* 4.4 WestfW: add initialization of address to keep */
|
||||
/* the compiler happy. Change SC'ed targets. */
|
||||
/* Return the SW version via READ PARAM */
|
||||
/* 4.3 WestfW: catch framing errors in getch(), so that */
|
||||
/* AVRISP works without HW kludges. */
|
||||
/* http://code.google.com/p/arduino/issues/detail?id=368n*/
|
||||
/* 4.2 WestfW: reduce code size, fix timeouts, change */
|
||||
/* verifySpace to use WDT instead of appstart */
|
||||
/* 4.1 WestfW: put version number in binary. */
|
||||
/**********************************************************/
|
||||
|
||||
#define OPTIBOOT_MAJVER 4
|
||||
#define OPTIBOOT_MINVER 5
|
||||
|
||||
#define MULTI_CALLED 1
|
||||
|
||||
#define MAKESTR(a) #a
|
||||
#define MAKEVER(a, b) MAKESTR(a*256+b)
|
||||
|
||||
// Page Size is 128 words (256 bytes)
|
||||
|
||||
//asm(" .section .version\n"
|
||||
// "optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
|
||||
// " .section .text\n");
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
// <avr/boot.h> uses sts instructions, but this version uses out instructions
|
||||
// This saves cycles and program memory.
|
||||
//#include "boot.h"
|
||||
|
||||
|
||||
// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
|
||||
|
||||
#include "pin_defs.h"
|
||||
#include "stk500.h"
|
||||
|
||||
#define BIND_pin 2 //PD2
|
||||
#define BIND_port PORTD
|
||||
#define IS_BIND_BUTTON_on ( (BIND_port.IN & _BV(BIND_pin)) == 0x00 )
|
||||
|
||||
#ifndef LED_START_FLASHES
|
||||
#define LED_START_FLASHES 0
|
||||
#endif
|
||||
|
||||
#ifdef LUDICROUS_SPEED
|
||||
#define BAUD_RATE 230400L
|
||||
#endif
|
||||
|
||||
/* set the UART baud rate defaults */
|
||||
#ifndef BAUD_RATE
|
||||
#if F_CPU >= 8000000L
|
||||
#define BAUD_RATE 38400L // Highest rate Avrdude win32 will support
|
||||
#elsif F_CPU >= 1000000L
|
||||
#define BAUD_RATE 9600L // 19200 also supported, but with significant error
|
||||
#elsif F_CPU >= 128000L
|
||||
#define BAUD_RATE 4800L // Good for 128kHz internal RC
|
||||
#else
|
||||
#define BAUD_RATE 1200L // Good even at 32768Hz
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef UART
|
||||
#define UART 0
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Switch in soft UART for hard baud rates */
|
||||
/*
|
||||
* I don't understand what this was supposed to accomplish, where the
|
||||
* constant "280" came from, or why automatically (and perhaps unexpectedly)
|
||||
* switching to a soft uart is a good thing, so I'm undoing this in favor
|
||||
* of a range check using the same calc used to config the BRG...
|
||||
*/
|
||||
#if (F_CPU/BAUD_RATE) > 280 // > 57600 for 16MHz
|
||||
#ifndef SOFT_UART
|
||||
#define SOFT_UART
|
||||
#endif
|
||||
#endif
|
||||
#else // 0
|
||||
#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 > 250
|
||||
#error Unachievable baud rate (too slow) BAUD_RATE
|
||||
#endif // baud rate slow check
|
||||
#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 < 3
|
||||
#error Unachievable baud rate (too fast) BAUD_RATE
|
||||
#endif // baud rate fastn check
|
||||
#endif
|
||||
|
||||
/* Watchdog settings */
|
||||
#define WATCHDOG_OFF (0)
|
||||
#define WATCHDOG_16MS (_BV(WDE))
|
||||
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
|
||||
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
|
||||
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
|
||||
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
|
||||
#ifndef __AVR_ATmega8__
|
||||
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
|
||||
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
||||
#endif
|
||||
|
||||
/* Function Prototypes */
|
||||
/* The main function is in init9, which removes the interrupt vector table */
|
||||
/* we don't need. It is also 'naked', which means the compiler does not */
|
||||
/* generate any entry or exit code itself. */
|
||||
int main(void) __attribute__ ((OS_main)) __attribute__ ((noreturn)) __attribute__ ((section (".init9")));
|
||||
void putch(char);
|
||||
uint8_t getch(void);
|
||||
static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */
|
||||
void verifySpace();
|
||||
#if LED_START_FLASHES > 0
|
||||
static inline void flash_led(uint8_t);
|
||||
#endif
|
||||
uint8_t getLen();
|
||||
//static inline void watchdogReset();
|
||||
void watchdogConfig(uint8_t x);
|
||||
#ifdef SOFT_UART
|
||||
void uartDelay() __attribute__ ((naked));
|
||||
#endif
|
||||
static void appStart() ; // __attribute__ ((naked));
|
||||
void boot_spm_busy_wait() ;
|
||||
void __boot_page_erase_short( uint16_t address ) ;
|
||||
void __boot_page_fill_short( uint16_t address, uint16_t data) ;
|
||||
void __boot_page_write_short( uint16_t address) ;
|
||||
void __boot_erase_flash_buffer( uint16_t address ) ;
|
||||
void init() ;
|
||||
|
||||
/*
|
||||
* NRWW memory
|
||||
* Addresses below NRWW (Non-Read-While-Write) can be programmed while
|
||||
* continuing to run code from flash, slightly speeding up programming
|
||||
* time. Beware that Atmel data sheets specify this as a WORD address,
|
||||
* while optiboot will be comparing against a 16-bit byte address. This
|
||||
* means that on a part with 128kB of memory, the upper part of the lower
|
||||
* 64k will get NRWW processing as well, even though it doesn't need it.
|
||||
* That's OK. In fact, you can disable the overlapping processing for
|
||||
* a part entirely by setting NRWWSTART to zero. This reduces code
|
||||
* space a bit, at the expense of being slightly slower, overall.
|
||||
*
|
||||
* RAMSTART should be self-explanatory. It's bigger on parts with a
|
||||
* lot of peripheral registers.
|
||||
*/
|
||||
#if defined(__AVR_ATmega168__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x3800)
|
||||
#elif defined(__AVR_ATmega328P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x7000)
|
||||
#elif defined(__AVR_ATmega328__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x7000)
|
||||
#elif defined (__AVR_ATmega644P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0xE000)
|
||||
// correct for a bug in avr-libc
|
||||
#undef SIGNATURE_2
|
||||
#define SIGNATURE_2 0x0A
|
||||
#elif defined (__AVR_ATmega1284P__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0xE000)
|
||||
#elif defined(__AVR_ATtiny84__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x0000)
|
||||
#elif defined(__AVR_ATmega1280__)
|
||||
#define RAMSTART (0x200)
|
||||
#define NRWWSTART (0xE000)
|
||||
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
||||
#define RAMSTART (0x100)
|
||||
#define NRWWSTART (0x1800)
|
||||
#endif
|
||||
|
||||
/* C zero initialises all global variables. However, that requires */
|
||||
/* These definitions are NOT zero initialised, but that doesn't matter */
|
||||
/* This allows us to drop the zero init code, saving us memory */
|
||||
#define buff ((uint8_t*)(RAMSTART))
|
||||
#ifdef VIRTUAL_BOOT_PARTITION
|
||||
#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4))
|
||||
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle devices with up to 4 uarts (eg m1280.) Rather inelegantly.
|
||||
* Note that mega8 still needs special handling, because ubrr is handled
|
||||
* differently.
|
||||
*/
|
||||
#if UART == 0
|
||||
# define UART_SRA UCSR0A
|
||||
# define UART_SRB UCSR0B
|
||||
# define UART_SRC UCSR0C
|
||||
# define UART_SRL UBRR0L
|
||||
# define UART_UDR UDR0
|
||||
#elif UART == 1
|
||||
# define UART_SRA UCSR1A
|
||||
# define UART_SRB UCSR1B
|
||||
# define UART_SRC UCSR1C
|
||||
# define UART_SRL UBRR1L
|
||||
# define UART_UDR UDR1
|
||||
#elif UART == 2
|
||||
# define UART_SRA UCSR2A
|
||||
# define UART_SRB UCSR2B
|
||||
# define UART_SRC UCSR2C
|
||||
# define UART_SRL UBRR2L
|
||||
# define UART_UDR UDR2
|
||||
#elif UART == 3
|
||||
# define UART_SRA UCSR3A
|
||||
# define UART_SRB UCSR3B
|
||||
# define UART_SRC UCSR3C
|
||||
# define UART_SRL UBRR3L
|
||||
# define UART_UDR UDR3
|
||||
#endif
|
||||
|
||||
|
||||
/* main program starts here */
|
||||
int main(void)
|
||||
{
|
||||
uint8_t ch;
|
||||
uint8_t byte ;
|
||||
|
||||
/*
|
||||
* Making these local and in registers prevents the need for initializing
|
||||
* them, and also saves space because code no longer stores to memory.
|
||||
* (initializing address keeps the compiler happy, but isn't really
|
||||
* necessary, and uses 4 bytes of flash.)
|
||||
*/
|
||||
register uint16_t address = 0;
|
||||
init() ;
|
||||
|
||||
// After the zero init loop, this is the first code to run.
|
||||
//
|
||||
// This code makes the following assumptions:
|
||||
// No interrupts will execute
|
||||
// SP points to RAMEND
|
||||
// r1 contains zero
|
||||
//
|
||||
// If not, uncomment the following instructions:
|
||||
// cli();
|
||||
asm volatile ("clr __zero_reg__");
|
||||
|
||||
ch = RST.STATUS ;
|
||||
RST.STATUS = 0xFF ; // Clear all flags
|
||||
|
||||
// Here, if power on, wait 0.1 secs, then check for
|
||||
// serial Rx signal low, if so, stay in bootloader
|
||||
// else go to application
|
||||
|
||||
if (ch & (RST_EXTRF_bm | RST_PORF_bm ) )
|
||||
{
|
||||
TCC1.CCA = 25000 ;
|
||||
TCC1.INTFLAGS = TC1_CCAIF_bm ;
|
||||
|
||||
while(!(TCC1.INTFLAGS & TC1_CCAIF_bm))
|
||||
;
|
||||
TCC1.CTRLA = 0 ; // Stop timer
|
||||
|
||||
uint8_t x ;
|
||||
x = PORTD.IN & 0x04 ;
|
||||
if ( x != 0 )
|
||||
{
|
||||
appStart() ; // Power on, go to app if loaded
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#ifndef SOFT_UART
|
||||
// UART_SRA = _BV(U2X0); //Double speed mode USART0
|
||||
// UART_SRB = _BV(RXEN0) | _BV(TXEN0);
|
||||
// UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
|
||||
//// UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
|
||||
//// Baudrate of 57600
|
||||
//#if F_CPU == 12000000L
|
||||
// UART_SRL = 25 ;
|
||||
//#else
|
||||
//#if F_CPU == 16000000L
|
||||
// UART_SRL = 33 ;
|
||||
//#else
|
||||
//#ERROR Baud rate not available
|
||||
//#endif
|
||||
//#endif
|
||||
|
||||
//#endif
|
||||
|
||||
// Set up watchdog to trigger after 500ms
|
||||
|
||||
|
||||
// watchdogConfig(WATCHDOG_1S);
|
||||
|
||||
/* Set LED pin as output */
|
||||
#define LED_pin 1 //PD1
|
||||
#define LED_port PORTD
|
||||
LED_port.DIRSET = _BV(LED_pin) ;
|
||||
|
||||
//#ifdef SOFT_UART
|
||||
// /* Set TX pin as output */
|
||||
// UART_DDR |= _BV(UART_TX_BIT);
|
||||
//#endif
|
||||
|
||||
//#if LED_START_FLASHES > 0
|
||||
// /* Flash onboard LED to signal entering of bootloader */
|
||||
// flash_led(LED_START_FLASHES * 2);
|
||||
//#endif
|
||||
|
||||
/* Forever loop */
|
||||
for (;;)
|
||||
{
|
||||
/* get character from UART */
|
||||
ch = getch();
|
||||
|
||||
if(ch == STK_GET_PARAMETER)
|
||||
{
|
||||
byte = getch();
|
||||
verifySpace();
|
||||
if ( byte == 0x82)
|
||||
{
|
||||
/*
|
||||
* Send optiboot version as "minor SW version"
|
||||
*/
|
||||
putch(OPTIBOOT_MINVER);
|
||||
}
|
||||
else if ( byte == 0x81)
|
||||
{
|
||||
putch(OPTIBOOT_MAJVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* GET PARAMETER returns a generic 0x03 reply for
|
||||
* other parameters - enough to keep Avrdude happy
|
||||
*/
|
||||
putch(0x03);
|
||||
}
|
||||
}
|
||||
else if(ch == STK_SET_DEVICE) {
|
||||
// SET DEVICE is ignored
|
||||
getNch(20);
|
||||
}
|
||||
else if(ch == STK_SET_DEVICE_EXT)
|
||||
{
|
||||
// SET DEVICE EXT is ignored
|
||||
getNch(5);
|
||||
}
|
||||
else if(ch == STK_LOAD_ADDRESS)
|
||||
{
|
||||
// LOAD ADDRESS
|
||||
uint16_t newAddress;
|
||||
newAddress = getch() ;
|
||||
newAddress = (newAddress & 0xff) | (getch() << 8);
|
||||
#ifdef RAMPZ
|
||||
// Transfer top bit to RAMPZ
|
||||
RAMPZ = (newAddress & 0x8000) ? 1 : 0;
|
||||
#endif
|
||||
// newAddress += newAddress; // Convert from word address to byte address
|
||||
address = newAddress;
|
||||
verifySpace();
|
||||
}
|
||||
else if(ch == STK_UNIVERSAL)
|
||||
{
|
||||
// UNIVERSAL command is ignored
|
||||
getNch(4);
|
||||
putch(0x00);
|
||||
}
|
||||
/* Write memory, length is big endian and is in bytes */
|
||||
else if(ch == STK_PROG_PAGE)
|
||||
{
|
||||
// PROGRAM PAGE - we support flash programming only, not EEPROM
|
||||
uint8_t *bufPtr;
|
||||
uint16_t addrPtr;
|
||||
register uint8_t length;
|
||||
|
||||
getch(); /* getlen() */
|
||||
length = getch();
|
||||
getch();
|
||||
|
||||
// If we are in RWW section, immediately start page erase
|
||||
// if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
||||
__boot_page_erase_short((uint16_t)(void*)address);
|
||||
|
||||
// While that is going on, read in page contents
|
||||
bufPtr = buff;
|
||||
do *bufPtr++ = getch();
|
||||
while (--length);
|
||||
|
||||
// If we are in NRWW section, page erase has to be delayed until now.
|
||||
// Todo: Take RAMPZ into account
|
||||
//#ifdef MULTI_CALLED
|
||||
// if (address < 0x7E00)
|
||||
//#endif
|
||||
// {
|
||||
// if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
||||
// }
|
||||
// Read command terminator, start reply
|
||||
verifySpace();
|
||||
|
||||
// If only a partial page is to be programmed, the erase might not be complete.
|
||||
// So check that here
|
||||
#ifdef MULTI_CALLED
|
||||
if (address < 0x8000)
|
||||
#endif
|
||||
{
|
||||
boot_spm_busy_wait();
|
||||
|
||||
#ifdef VIRTUAL_BOOT_PARTITION
|
||||
if ((uint16_t)(void*)address == 0) {
|
||||
// This is the reset vector page. We need to live-patch the code so the
|
||||
// bootloader runs.
|
||||
//
|
||||
// Move RESET vector to WDT vector
|
||||
uint16_t vect = buff[0] | (buff[1]<<8);
|
||||
rstVect = vect;
|
||||
wdtVect = buff[8] | (buff[9]<<8);
|
||||
vect -= 4; // Instruction is a relative jump (rjmp), so recalculate.
|
||||
buff[8] = vect & 0xff;
|
||||
buff[9] = vect >> 8;
|
||||
|
||||
// Add jump to bootloader at RESET vector
|
||||
buff[0] = 0x7f;
|
||||
buff[1] = 0xce; // rjmp 0x1d00 instruction
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy buffer into programming buffer
|
||||
bufPtr = buff;
|
||||
addrPtr = (uint16_t)(void*)address;
|
||||
ch = SPM_PAGESIZE / 2;
|
||||
__boot_erase_flash_buffer((uint16_t)(void*)addrPtr ) ;
|
||||
do {
|
||||
uint16_t a;
|
||||
// a = *bufPtr++;
|
||||
// a |= (*bufPtr++) << 8;
|
||||
|
||||
a = *((uint16_t *)bufPtr) ;
|
||||
bufPtr += 2 ;
|
||||
|
||||
__boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
||||
addrPtr += 2;
|
||||
} while (--ch);
|
||||
|
||||
// Write from programming buffer
|
||||
__boot_page_write_short((uint16_t)(void*)address);
|
||||
boot_spm_busy_wait();
|
||||
|
||||
#if defined(RWWSRE)
|
||||
// Reenable read access to flash
|
||||
boot_rww_enable();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Read memory block mode, length is big endian. */
|
||||
else if(ch == STK_READ_PAGE)
|
||||
{
|
||||
register uint8_t length;
|
||||
// READ PAGE - we only read flash
|
||||
getch(); /* getlen() */
|
||||
length = getch();
|
||||
getch();
|
||||
|
||||
verifySpace();
|
||||
//#ifdef VIRTUAL_BOOT_PARTITION
|
||||
// do {
|
||||
// // Undo vector patch in bottom page so verify passes
|
||||
// if (address == 0) ch=rstVect & 0xff;
|
||||
// else if (address == 1) ch=rstVect >> 8;
|
||||
// else if (address == 8) ch=wdtVect & 0xff;
|
||||
// else if (address == 9) ch=wdtVect >> 8;
|
||||
// else ch = pgm_read_byte_near(address);
|
||||
// address++;
|
||||
// putch(ch);
|
||||
// } while (--length);
|
||||
//#else
|
||||
//#ifdef RAMPZ
|
||||
//// Since RAMPZ should already be set, we need to use EPLM directly.
|
||||
//// do putch(pgm_read_byte_near(address++));
|
||||
//// while (--length);
|
||||
// do {
|
||||
// uint8_t result;
|
||||
// __asm__ ("elpm %0,Z\n":"=r"(result):"z"(address));
|
||||
// putch(result);
|
||||
// address++;
|
||||
// }
|
||||
// while (--length);
|
||||
//#else
|
||||
do putch(pgm_read_byte_near(address++));
|
||||
while (--length);
|
||||
//#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
/* Get device signature bytes */
|
||||
else if(ch == STK_READ_SIGN)
|
||||
{
|
||||
// READ SIGN - return what Avrdude wants to hear
|
||||
verifySpace();
|
||||
putch(SIGNATURE_0);
|
||||
putch(SIGNATURE_1);
|
||||
putch(SIGNATURE_2);
|
||||
}
|
||||
else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
|
||||
// Adaboot no-wait mod
|
||||
|
||||
// watchdogConfig(WATCHDOG_16MS);
|
||||
|
||||
verifySpace();
|
||||
#ifdef MULTI_CALLED
|
||||
putch(STK_OK);
|
||||
while(!(USARTC0.STATUS & USART_TXCIF_bm))
|
||||
;
|
||||
appStart() ;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// This covers the response to commands like STK_ENTER_PROGMODE
|
||||
verifySpace();
|
||||
}
|
||||
putch(STK_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void putch(char ch)
|
||||
{
|
||||
//#ifndef SOFT_UART
|
||||
while(!(USARTC0.STATUS & USART_DREIF_bm))
|
||||
;
|
||||
USARTC0.DATA = ch ;
|
||||
//#else
|
||||
// __asm__ __volatile__ (
|
||||
// " com %[ch]\n" // ones complement, carry set
|
||||
// " sec\n"
|
||||
// "1: brcc 2f\n"
|
||||
// " cbi %[uartPort],%[uartBit]\n"
|
||||
// " rjmp 3f\n"
|
||||
// "2: sbi %[uartPort],%[uartBit]\n"
|
||||
// " nop\n"
|
||||
// "3: rcall uartDelay\n"
|
||||
// " rcall uartDelay\n"
|
||||
// " lsr %[ch]\n"
|
||||
// " dec %[bitcnt]\n"
|
||||
// " brne 1b\n"
|
||||
// :
|
||||
// :
|
||||
// [bitcnt] "d" (10),
|
||||
// [ch] "r" (ch),
|
||||
// [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
|
||||
// [uartBit] "I" (UART_TX_BIT)
|
||||
// :
|
||||
// "r25"
|
||||
// );
|
||||
//#endif
|
||||
}
|
||||
|
||||
uint8_t getch(void)
|
||||
{
|
||||
uint8_t ch;
|
||||
|
||||
//#ifdef LED_DATA_FLASH
|
||||
//#ifdef __AVR_ATmega8__
|
||||
// LED_PORT ^= _BV(LED);
|
||||
//#else
|
||||
// LED_PIN |= _BV(LED);
|
||||
//#endif
|
||||
//#endif
|
||||
|
||||
//#ifdef SOFT_UART
|
||||
// __asm__ __volatile__ (
|
||||
// "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
|
||||
// " rjmp 1b\n"
|
||||
// " rcall uartDelay\n" // Get to middle of start bit
|
||||
// "2: rcall uartDelay\n" // Wait 1 bit period
|
||||
// " rcall uartDelay\n" // Wait 1 bit period
|
||||
// " clc\n"
|
||||
// " sbic %[uartPin],%[uartBit]\n"
|
||||
// " sec\n"
|
||||
// " dec %[bitCnt]\n"
|
||||
// " breq 3f\n"
|
||||
// " ror %[ch]\n"
|
||||
// " rjmp 2b\n"
|
||||
// "3:\n"
|
||||
// :
|
||||
// [ch] "=r" (ch)
|
||||
// :
|
||||
// [bitCnt] "d" (9),
|
||||
// [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
|
||||
// [uartBit] "I" (UART_RX_BIT)
|
||||
// :
|
||||
// "r25"
|
||||
//);
|
||||
//#else
|
||||
while(!(USARTC0.STATUS & USART_RXCIF_bm))
|
||||
|
||||
// watchdogReset()
|
||||
|
||||
;
|
||||
// if (!(UART_SRA & _BV(FE0))) {
|
||||
/*
|
||||
* A Framing Error indicates (probably) that something is talking
|
||||
* to us at the wrong bit rate. Assume that this is because it
|
||||
* expects to be talking to the application, and DON'T reset the
|
||||
* watchdog. This should cause the bootloader to abort and run
|
||||
* the application "soon", if it keeps happening. (Note that we
|
||||
* don't care that an invalid char is returned...)
|
||||
*/
|
||||
// watchdogReset();
|
||||
// }
|
||||
|
||||
ch = USARTC0.DATA ;
|
||||
//#endif
|
||||
|
||||
//#ifdef LED_DATA_FLASH
|
||||
//#ifdef __AVR_ATmega8__
|
||||
// LED_PORT ^= _BV(LED);
|
||||
//#else
|
||||
// LED_PIN |= _BV(LED);
|
||||
//#endif
|
||||
//#endif
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
#ifdef SOFT_UART
|
||||
// AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
|
||||
// Adding 3 to numerator simulates nearest rounding for more accurate baud rates
|
||||
#define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6)
|
||||
#if UART_B_VALUE > 255
|
||||
#error Baud rate too slow for soft UART
|
||||
#endif
|
||||
|
||||
void uartDelay() {
|
||||
__asm__ __volatile__ (
|
||||
"ldi r25,%[count]\n"
|
||||
"1:dec r25\n"
|
||||
"brne 1b\n"
|
||||
"ret\n"
|
||||
::[count] "M" (UART_B_VALUE)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
void getNch(uint8_t count) {
|
||||
do getch(); while (--count);
|
||||
verifySpace();
|
||||
}
|
||||
|
||||
void verifySpace()
|
||||
{
|
||||
if ( getch() != CRC_EOP) {
|
||||
|
||||
putch(STK_NOSYNC);
|
||||
// watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
|
||||
//
|
||||
// while (1) // and busy-loop so that WD causes
|
||||
// ; // a reset and app start.
|
||||
}
|
||||
putch(STK_INSYNC);
|
||||
}
|
||||
|
||||
#if LED_START_FLASHES > 0
|
||||
void flash_led(uint8_t count) {
|
||||
do {
|
||||
TCNT1 = -(F_CPU/(1024*16));
|
||||
TIFR1 = _BV(TOV1);
|
||||
while(!(TIFR1 & _BV(TOV1)));
|
||||
//#ifdef __AVR_ATmega8__
|
||||
LED_PORT ^= _BV(LED);
|
||||
//#else
|
||||
// LED_PIN |= _BV(LED);
|
||||
//#endif
|
||||
watchdogReset();
|
||||
} while (--count);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Watchdog functions. These are only safe with interrupts turned off.
|
||||
void watchdogReset() {
|
||||
__asm__ __volatile__ (
|
||||
"wdr\n"
|
||||
);
|
||||
}
|
||||
|
||||
//void watchdogConfig(uint8_t x) {
|
||||
// WDTCSR = _BV(WDCE) | _BV(WDE);
|
||||
// WDTCSR = x;
|
||||
//}
|
||||
|
||||
void init()
|
||||
{
|
||||
// Enable external oscillator (16MHz)
|
||||
OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_256CLK_gc ;
|
||||
OSC.CTRL |= OSC_XOSCEN_bm ;
|
||||
while( ( OSC.STATUS & OSC_XOSCRDY_bm ) == 0 )
|
||||
/* wait */ ;
|
||||
// Enable PLL (*2 = 32MHz)
|
||||
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2 ;
|
||||
OSC.CTRL |= OSC_PLLEN_bm ;
|
||||
while( ( OSC.STATUS & OSC_PLLRDY_bm ) == 0 )
|
||||
/* wait */ ;
|
||||
// Switch to PLL clock
|
||||
CPU_CCP = 0xD8 ;
|
||||
CLK.CTRL = CLK_SCLKSEL_RC2M_gc ;
|
||||
CPU_CCP = 0xD8 ;
|
||||
CLK.CTRL = CLK_SCLKSEL_PLL_gc ;
|
||||
|
||||
PMIC.CTRL = 7 ; // Enable all interrupt levels
|
||||
|
||||
|
||||
// Timer1 config
|
||||
// TCC1 16-bit timer, clocked at 0.5uS
|
||||
EVSYS.CH3MUX = 0x80 + 0x07 ; // Prescaler of 128
|
||||
TCC1.CTRLB = 0; TCC1.CTRLC = 0; TCC1.CTRLD = 0; TCC1.CTRLE = 0;
|
||||
TCC1.INTCTRLA = 0 ;
|
||||
TCC1.INTCTRLB = 0 ;
|
||||
TCC1.PER = 0xFFFF ;
|
||||
TCC1.CNT = 0 ;
|
||||
TCC1.CTRLA = 0x0B ; // Event3 (prescale of 16)
|
||||
|
||||
PORTD.OUTSET = 0x04 ;
|
||||
PORTD.DIRCLR = 0x04 ;
|
||||
PORTD.PIN2CTRL = 0x18 ; // Pullup
|
||||
|
||||
PORTC.OUTSET = 0x08 ;
|
||||
PORTC.DIRSET = 0x08 ;
|
||||
|
||||
USARTC0.BAUDCTRLA = 34 ; // 57600
|
||||
USARTC0.BAUDCTRLB = 0 ;
|
||||
|
||||
USARTC0.CTRLB = 0x18 ; // Enable Tx and Rx
|
||||
USARTC0.CTRLA = (USARTC0.CTRLA & 0xCF) ;
|
||||
USARTC0.CTRLC = 0x03 ; // 8 bit, no parity, 1 stop
|
||||
USARTC0.DATA ;
|
||||
}
|
||||
|
||||
void boot_spm_busy_wait()
|
||||
{
|
||||
while(NVM.STATUS & NVM_NVMBUSY_bm)
|
||||
;
|
||||
}
|
||||
|
||||
#define A_NVM_CMD 0x1CA
|
||||
|
||||
void __boot_page_erase_short( uint16_t address )
|
||||
{
|
||||
asm( "push r24" ) ;
|
||||
asm( "push r25" ) ;
|
||||
NVM.CMD = NVM_CMD_ERASE_APP_PAGE_gc ;
|
||||
asm( "pop r31" ) ;
|
||||
asm( "pop r30" ) ;
|
||||
CCP = CCP_SPM_gc ;
|
||||
asm( "spm" ) ;
|
||||
}
|
||||
|
||||
void __boot_erase_flash_buffer( uint16_t address )
|
||||
{
|
||||
asm( "movw r30,r24" ) ;
|
||||
asm( "ldi r24,0x26" ) ;
|
||||
asm("sts 0x1CA,r24");
|
||||
CCP = CCP_IOREG_gc ;
|
||||
asm( "ldi r24,1" ) ;
|
||||
asm("sts 0x1CB,r24");
|
||||
}
|
||||
|
||||
void __boot_page_fill_short( uint16_t address, uint16_t data)
|
||||
{
|
||||
asm( "push r0" ) ;
|
||||
asm( "push r1" ) ;
|
||||
asm( "movw r30,r24" ) ;
|
||||
asm( "mov r0,r22" ) ;
|
||||
asm( "mov r1,r23" ) ;
|
||||
asm( "ldi r24,0x23" ) ;
|
||||
asm("sts 0x1CA,r24");
|
||||
CCP = CCP_SPM_gc ;
|
||||
asm( "spm" ) ;
|
||||
asm( "pop r1" ) ;
|
||||
asm( "pop r0" ) ;
|
||||
}
|
||||
|
||||
void __boot_page_write_short( uint16_t address)
|
||||
{
|
||||
asm( "movw r30,r24" ) ;
|
||||
asm( "ldi r24,0x2E" ) ;
|
||||
asm("sts 0x1CA,r24");
|
||||
CCP = CCP_SPM_gc ;
|
||||
asm( "spm" ) ;
|
||||
}
|
||||
|
||||
void appStart()
|
||||
{
|
||||
// watchdogConfig(WATCHDOG_OFF);
|
||||
// __asm__ __volatile__ (
|
||||
//#ifdef VIRTUAL_BOOT_PARTITION
|
||||
// // Jump to WDT vector
|
||||
// "ldi r30,4\n"
|
||||
// "clr r31\n"
|
||||
//#else
|
||||
// // Jump to RST vector
|
||||
// "clr r30\n"
|
||||
// "clr r31\n"
|
||||
//#endif
|
||||
// "ijmp\n"
|
||||
// );
|
||||
|
||||
register void (*p)() ;
|
||||
p = 0 ;
|
||||
|
||||
if ( pgm_read_byte( (uint16_t)p ) != 0xFF )
|
||||
{
|
||||
(*p)() ;
|
||||
}
|
||||
}
|
||||
|
||||
80
BootLoaders/OrangeMultiBoot/Source/pin_defs.h
Normal file
80
BootLoaders/OrangeMultiBoot/Source/pin_defs.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
||||
/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB5
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTD
|
||||
#define UART_PIN PIND
|
||||
#define UART_DDR DDRD
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__AVR_ATmega8__)
|
||||
//Name conversion R.Wiersma
|
||||
#define UCSR0A UCSRA
|
||||
#define UDR0 UDR
|
||||
#define UDRE0 UDRE
|
||||
#define RXC0 RXC
|
||||
#define FE0 FE
|
||||
#define TIFR1 TIFR
|
||||
#define WDTCSR WDTCR
|
||||
#endif
|
||||
|
||||
/* Luminet support */
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
/* Red LED is connected to pin PA4 */
|
||||
#define LED_DDR DDRA
|
||||
#define LED_PORT PORTA
|
||||
#define LED_PIN PINA
|
||||
#define LED PINA4
|
||||
/* Ports for soft UART - left port only for now. TX/RX on PA2/PA3 */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTA
|
||||
#define UART_PIN PINA
|
||||
#define UART_DDR DDRA
|
||||
#define UART_TX_BIT 2
|
||||
#define UART_RX_BIT 3
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Sanguino support */
|
||||
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
|
||||
/* Onboard LED is connected to pin PB0 on Sanguino */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB0
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTD
|
||||
#define UART_PIN PIND
|
||||
#define UART_DDR DDRD
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Mega support */
|
||||
#if defined(__AVR_ATmega1280__)
|
||||
/* Onboard LED is connected to pin PB7 on Arduino Mega */
|
||||
#define LED_DDR DDRB
|
||||
#define LED_PORT PORTB
|
||||
#define LED_PIN PINB
|
||||
#define LED PINB7
|
||||
|
||||
/* Ports for soft UART */
|
||||
#ifdef SOFT_UART
|
||||
#define UART_PORT PORTE
|
||||
#define UART_PIN PINE
|
||||
#define UART_DDR DDRE
|
||||
#define UART_TX_BIT 1
|
||||
#define UART_RX_BIT 0
|
||||
#endif
|
||||
#endif
|
||||
39
BootLoaders/OrangeMultiBoot/Source/stk500.h
Normal file
39
BootLoaders/OrangeMultiBoot/Source/stk500.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* STK500 constants list, from AVRDUDE */
|
||||
#define STK_OK 0x10
|
||||
#define STK_FAILED 0x11 // Not used
|
||||
#define STK_UNKNOWN 0x12 // Not used
|
||||
#define STK_NODEVICE 0x13 // Not used
|
||||
#define STK_INSYNC 0x14 // ' '
|
||||
#define STK_NOSYNC 0x15 // Not used
|
||||
#define ADC_CHANNEL_ERROR 0x16 // Not used
|
||||
#define ADC_MEASURE_OK 0x17 // Not used
|
||||
#define PWM_CHANNEL_ERROR 0x18 // Not used
|
||||
#define PWM_ADJUST_OK 0x19 // Not used
|
||||
#define CRC_EOP 0x20 // 'SPACE'
|
||||
#define STK_GET_SYNC 0x30 // '0'
|
||||
#define STK_GET_SIGN_ON 0x31 // '1'
|
||||
#define STK_SET_PARAMETER 0x40 // '@'
|
||||
#define STK_GET_PARAMETER 0x41 // 'A'
|
||||
#define STK_SET_DEVICE 0x42 // 'B'
|
||||
#define STK_SET_DEVICE_EXT 0x45 // 'E'
|
||||
#define STK_ENTER_PROGMODE 0x50 // 'P'
|
||||
#define STK_LEAVE_PROGMODE 0x51 // 'Q'
|
||||
#define STK_CHIP_ERASE 0x52 // 'R'
|
||||
#define STK_CHECK_AUTOINC 0x53 // 'S'
|
||||
#define STK_LOAD_ADDRESS 0x55 // 'U'
|
||||
#define STK_UNIVERSAL 0x56 // 'V'
|
||||
#define STK_PROG_FLASH 0x60 // '`'
|
||||
#define STK_PROG_DATA 0x61 // 'a'
|
||||
#define STK_PROG_FUSE 0x62 // 'b'
|
||||
#define STK_PROG_LOCK 0x63 // 'c'
|
||||
#define STK_PROG_PAGE 0x64 // 'd'
|
||||
#define STK_PROG_FUSE_EXT 0x65 // 'e'
|
||||
#define STK_READ_FLASH 0x70 // 'p'
|
||||
#define STK_READ_DATA 0x71 // 'q'
|
||||
#define STK_READ_FUSE 0x72 // 'r'
|
||||
#define STK_READ_LOCK 0x73 // 's'
|
||||
#define STK_READ_PAGE 0x74 // 't'
|
||||
#define STK_READ_SIGN 0x75 // 'u'
|
||||
#define STK_READ_OSCCAL 0x76 // 'v'
|
||||
#define STK_READ_FUSE_EXT 0x77 // 'w'
|
||||
#define STK_READ_OSCCAL_EXT 0x78 // 'x'
|
||||
2
BootLoaders/README.md
Normal file
2
BootLoaders/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
## Page Moved
|
||||
Moved to [/docs/Arduino_IDE_Boards.md](/docs/Arduino_IDE_Boards.md).
|
||||
1
BootLoaders/StmMultiBoot/README.md
Normal file
1
BootLoaders/StmMultiBoot/README.md
Normal file
@@ -0,0 +1 @@
|
||||
[Source for the StmMultiBooloader](https://github.com/MikeBland/StmMultiBoot)
|
||||
BIN
BootLoaders/StmMultiBoot/StmMultiBoot.bin
Normal file
BIN
BootLoaders/StmMultiBoot/StmMultiBoot.bin
Normal file
Binary file not shown.
2
BootLoaders/StmMultiUSB/README.md
Normal file
2
BootLoaders/StmMultiUSB/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[Source for the StmMultiUSB=STM32duino-bootloader](https://github.com/rogerclarkmelbourne/STM32duino-bootloader)
|
||||
If you want the latest version, you should look for the file generic_boot20_pa1.bin.
|
||||
BIN
BootLoaders/StmMultiUSB/StmMultiUSB.bin
Normal file
BIN
BootLoaders/StmMultiUSB/StmMultiUSB.bin
Normal file
Binary file not shown.
657
BootLoaders/package_multi_4in1_board_index.json
Normal file
657
BootLoaders/package_multi_4in1_board_index.json
Normal file
@@ -0,0 +1,657 @@
|
||||
{
|
||||
"packages": [{
|
||||
"name": "multi4in1",
|
||||
"maintainer": "Pascal Langer",
|
||||
"websiteURL": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module",
|
||||
"email": "pascal_langer@yahoo.fr",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"platforms": [
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.2",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.2.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.2.tar.gz",
|
||||
"checksum": "SHA-256:b7e2fda37186bf696b7a769b12317737d513181096b33d9ad321ec2fd47f3f80",
|
||||
"size": "164467",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.3",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.3.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.3.tar.gz",
|
||||
"checksum": "SHA-256:7d4561eebe0d7f6422f06d5719a417e15fcc0aa9cdbfc1c48a57066ce768e33c",
|
||||
"size": "164483",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.4",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.4.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.4.tar.gz",
|
||||
"checksum": "SHA-256:6c51a4eb09bcd074cc651dab3f2356ea3afd358f6330aba0d8bdfaa75f718dbb",
|
||||
"size": "167975",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.5",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.5.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.5.tar.gz",
|
||||
"checksum": "SHA-256:0a4754d47cdbb49ca194b15835686331530ed9d36c0db093a29ae5f865e75421",
|
||||
"size": "169830",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.6",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.6.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.6.tar.gz",
|
||||
"checksum": "SHA-256:4f4cf8820e30bf6c88f280514c67ee67b9dc6649f439597cfb8d0be3a5b13bf5",
|
||||
"size": "169819",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.7",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.7.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.7.tar.gz",
|
||||
"checksum": "SHA-256:453c9999e433ed1bdda2ba2b12cb7cbba7b547591db969dc6b7efb941b61cf76",
|
||||
"size": "169825",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.8",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.8.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.8.tar.gz",
|
||||
"checksum": "SHA-256:8e58b8733220d56155e10bf5bec0bfe6bf96f8460b3fd49a4b45c7f9fad776cb",
|
||||
"size": "293388",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.0.9",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.0.9.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.0.9.tar.gz",
|
||||
"checksum": "SHA-256:269c4ddcb8018be2b31f5c9e9f0814d120af492e894b8d5098a814486d56faa5",
|
||||
"size": "318437",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.1.0",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.1.0.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.1.0.tar.gz",
|
||||
"checksum": "SHA-256:7bacf2db754ceb890a203de5ce89d97aa787a9e6462debeb44cf04830859687a",
|
||||
"size": "326431",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "MULTI-Module AVR Boards",
|
||||
"architecture": "avr",
|
||||
"version": "1.1.1",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_avr_board_v1.1.1.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_avr_board_v1.1.1.tar.gz",
|
||||
"checksum": "SHA-256:02158258b4dbaca61bedbb6933336200d13b02ad0db981e2dda253682c482e99",
|
||||
"size": "324512",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (Atmega328p, 3.3V, 16MHz)"},
|
||||
{"name": "Multi 4-in-1 (OrangeRX)"}
|
||||
],
|
||||
"toolsDependencies": []
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.1",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.1.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.1.tar.gz",
|
||||
"checksum": "SHA-256:b522b5d3474308768c197a6897cad037fb54d6fac26c75678415a0908793bae3",
|
||||
"size": "10332875",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.2",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.2.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.2.tar.gz",
|
||||
"checksum": "SHA-256:26d21dbd2fe80680ac523b8bca24b3ecf2c2016bac626826d20b651e11278287",
|
||||
"size": "10318646",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.3",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.3.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.3.tar.gz",
|
||||
"checksum": "SHA-256:e48f1f30948b3f7be83e8b1fe2bb5c6b41be7c4d5da02503a0b4827c60926541",
|
||||
"size": "10309833",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.4",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.4.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.4.tar.gz",
|
||||
"checksum": "SHA-256:388a4dbcd567f9d41b82955e12e8a640d9696217081c0ee6ab8c58a25aedf70f",
|
||||
"size": "10307581",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.5",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.5.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.5.tar.gz",
|
||||
"checksum": "SHA-256:46d3b4e62fc46e6b8ca4f429974ffd2ee8cde9e29a6e0cda58f85044991a9c2b",
|
||||
"size": "10313436",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.6",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.6.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.6.tar.gz",
|
||||
"checksum": "SHA-256:ad7a330326069a5ffb2908495b288933f68517b1247cc6636b160eb483a58284",
|
||||
"size": "10319669",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.7",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.7.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.7.tar.gz",
|
||||
"checksum": "SHA-256:f73fded48beaee55e646a3cf36d24beeedc336873c7824683a4912f2aee9e350",
|
||||
"size": "10322111",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.8",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.8.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.8.tar.gz",
|
||||
"checksum": "SHA-256:f8100272ec615074cf7962c2c8331014ebda78f3e4c17172b88b6dd3d83c4331",
|
||||
"size": "10319134",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.0.9",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.0.9.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.0.9.tar.gz",
|
||||
"checksum": "SHA-256:c3621d1cf6580ca5c943a67dc14dc15a60e2797a4b985548abe1919486bf4a8b",
|
||||
"size": "10324251",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.0",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.0.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.0.tar.gz",
|
||||
"checksum": "SHA-256:919ece2021757686e6892679956dcb8a01c9308a152167d61d9204656b4ed7ee",
|
||||
"size": "10333612",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.1",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.1.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.1.tar.gz",
|
||||
"checksum": "SHA-256:549dbfa0f48f3e519a9efa96d03e8933cc72989c618826b2b570980d9c382979",
|
||||
"size": "10331547",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.2",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.2.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.2.tar.gz",
|
||||
"checksum": "SHA-256:debfdc14df3023045a2297bc99daf7104be75f21572fc5a4f57192ffae4028f0",
|
||||
"size": "10322776",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.3",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.3.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.3.tar.gz",
|
||||
"checksum": "SHA-256:6b9dceb033ccc31f37cebc4f025ddb862cd24a733e7c356ca2fa5719d595af89",
|
||||
"size": "10322145",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.4",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.4.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.4.tar.gz",
|
||||
"checksum": "SHA-256:16a83a3b4409cb55aead6593396979483996080634d214ae07c8a956db2480fb",
|
||||
"size": "10322152",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.5",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.5.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.5.tar.gz",
|
||||
"checksum": "SHA-256:2d45c95f59b4fb9fc7f7bf8caca2dd8c13b4258141c20db6169e0c7faf72e5e4",
|
||||
"size": "7930904",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.6",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.6.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.6.tar.gz",
|
||||
"checksum": "SHA-256:d2d1ef721bbcdc3c680c6f98b4b8ab394478ac0f82d67af2f6c389a4a30789f4",
|
||||
"size": "7962942",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 STM32 Board",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.7",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.7.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.7.tar.gz",
|
||||
"checksum": "SHA-256:37cccde7eafad3d0587d28d13d5f8b2b3244bf7c83e6819b6cb08f4f468815e2",
|
||||
"size": "7966348",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (STM32F103C)"
|
||||
}],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi X-in-1 STM32 Boards",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.1.8",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.1.8.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.1.8.tar.gz",
|
||||
"checksum": "SHA-256:e9ed8055ebf72abf37e60e1b8d1c6ee5472132ea7c0a3c4a63fbb8442613e4c2",
|
||||
"size": "7481800",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (STM32F103C)"},
|
||||
{"name": "Multi 5-in-1 (Jumper T18 Internal)"}
|
||||
],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi X-in-1 STM32 Boards",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.2.0",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.2.0.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.2.0.tar.gz",
|
||||
"checksum": "SHA-256:754f08ca2a553701cc9112b645c079b6041107f1bf863648305e560c136a6ac5",
|
||||
"size": "7496214",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (STM32F103C)"},
|
||||
{"name": "Multi 5-in-1 (Jumper T18 Internal)"}
|
||||
],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi X-in-1 STM32 Boards",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.2.1",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.2.1.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.2.1.tar.gz",
|
||||
"checksum": "SHA-256:c66d34afadc5b21e9e28c4d477fa03a6d55db0b74b59ff2dcb07b4d6ef06da1a",
|
||||
"size": "7496448",
|
||||
"boards": [
|
||||
{"name": "Multi 4-in-1 (STM32F103C)"},
|
||||
{"name": "Multi 5-in-1 (Jumper T18 Internal)"}
|
||||
],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "MULTI-Module STM32 Boards",
|
||||
"architecture": "STM32F1",
|
||||
"version": "1.2.2",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_stm32_board_v1.2.2.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_stm32_board_v1.2.2.tar.gz",
|
||||
"checksum": "SHA-256:0fe4a8899438bbc31dc37714acca13968e43d75a47e59143343d83b634d2e47d",
|
||||
"size": "7485662",
|
||||
"boards": [
|
||||
{"name": "Multi X-in-1 STM32F103CB (128KB)"},
|
||||
{"name": "Multi X-in-1 STM32F103C8 (64KB)"},
|
||||
{"name": "Multi 5-in-1 (Jumper T18 Internal)"}
|
||||
],
|
||||
"toolsDependencies": [{
|
||||
"packager": "arduino",
|
||||
"name": "arm-none-eabi-gcc",
|
||||
"version": "4.8.3-2014q1"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Multi 4-in-1 OrangeRX Board - DEPRECATED, USE MULTI 4-IN-1 AVR BOARDS PACKAGE INSTEAD",
|
||||
"architecture": "orangerx",
|
||||
"version": "1.0.1",
|
||||
"category": "Contributed",
|
||||
"help": {
|
||||
"online": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module"
|
||||
},
|
||||
"url": "https://github.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/raw/master/archives/package_multi_4in1_orangerx_board_v1.0.1.tar.gz",
|
||||
"archiveFileName": "package_multi_4in1_orangerx_board_v1.0.1.tar.gz",
|
||||
"checksum": "SHA-256:7287ce61028b754bb8ff947317dd15773fc7eeecd752826c707fa356b9b36dc6",
|
||||
"size": "161615",
|
||||
"boards": [{
|
||||
"name": "Multi 4-in-1 (OrangeRX)"
|
||||
}],
|
||||
"toolsDependencies": []
|
||||
}
|
||||
],
|
||||
"tools": []
|
||||
}]
|
||||
}
|
||||
601
Lua_scripts/DSM FwdPrg.lua
Normal file
601
Lua_scripts/DSM FwdPrg.lua
Normal file
@@ -0,0 +1,601 @@
|
||||
local toolName = "TNS|DSM Forward Programming|TNE"
|
||||
|
||||
---- #########################################################################
|
||||
---- # #
|
||||
---- # Copyright (C) OpenTX #
|
||||
-----# #
|
||||
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
||||
---- # #
|
||||
---- # This program is free software; you can redistribute it and/or modify #
|
||||
---- # it under the terms of the GNU General Public License version 2 as #
|
||||
---- # published by the Free Software Foundation. #
|
||||
---- # #
|
||||
---- # This program is distributed in the hope that it will be useful #
|
||||
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
---- # GNU General Public License for more details. #
|
||||
---- # #
|
||||
---- #########################################################################
|
||||
|
||||
|
||||
--###############################################################################
|
||||
-- Multi buffer for DSM description
|
||||
-- Multi_Buffer[0..2]=="DSM" -> Lua script is running
|
||||
-- Multi_Buffer[3]==0x70+len -> TX to RX data ready to be sent
|
||||
-- Multi_Buffer[4..9]=6 bytes of TX to RX data
|
||||
-- Multi_Buffer[10..25]=16 bytes of RX to TX data
|
||||
--
|
||||
-- To start operation:
|
||||
-- Write 0x00 at address 3
|
||||
-- Write 0x00 at address 10
|
||||
-- Write "DSM" at address 0..2
|
||||
--###############################################################################
|
||||
|
||||
local RX_VERSION, WAIT_CMD, MENU_TITLE, MENU_LINES, MENU_VALUES, VALUE_CHANGING, VALUE_CHANGING_WAIT, VALUE_CHANGED = 0, 1, 2, 3, 4, 5, 6, 7
|
||||
local MENU, LIST_MENU_NOCHANGING, LIST_MENU2, PERCENTAGE_VALUE = 0x1C, 0x6C, 0x4C, 0xC0
|
||||
local Phase = RX_VERSION
|
||||
local Waiting_RX = 0
|
||||
local Text = {}
|
||||
local Retry=100
|
||||
local Blink = 0
|
||||
local Value_Changed=0
|
||||
|
||||
local Menu = { Cur=nil, Id=nil, Title="", Prev=nil, PrevId=nil, Next=nil, NextId=nil, Back=nil, BackId=nil, CurLine=nil, SelLine=nil, EditLine=nil }
|
||||
local Line = {}
|
||||
local RX = { Name="", Version="" }
|
||||
|
||||
local function conv_int16(number)
|
||||
if number >= 0x8000 then
|
||||
return number - 0x10000
|
||||
end
|
||||
return number
|
||||
end
|
||||
|
||||
local function Get_Text(index)
|
||||
out = Text[index]
|
||||
if out == nil then -- unknown...
|
||||
out = "Unknown_"..string.format("%X",index)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function DSM_Release()
|
||||
multiBuffer( 0, 0 )
|
||||
end
|
||||
|
||||
local function DSM_Send(...)
|
||||
local arg = {...}
|
||||
for i = 1 , #arg do
|
||||
multiBuffer( 3+i, arg[i])
|
||||
end
|
||||
multiBuffer( 3, 0x70+#arg)
|
||||
end
|
||||
|
||||
local function Value_Add(dir)
|
||||
local line=Line[Menu.SelLine]
|
||||
Speed = getRotEncSpeed()
|
||||
if Speed == ROTENC_MIDSPEED then
|
||||
line.Val = line.Val + (5 * dir)
|
||||
elseif Speed == ROTENC_HIGHSPEED then
|
||||
line.Val = line.Val + (15 * dir)
|
||||
else
|
||||
line.Val = line.Val + dir
|
||||
end
|
||||
if line.Val > line.Max then
|
||||
line.Val = line.Max
|
||||
elseif line.Val < line.Min then
|
||||
line.Val = line.Min
|
||||
end
|
||||
if Line[Menu.SelLine].Type ~= LIST_MENU_NOCHANGING then
|
||||
Phase = VALUE_CHANGING
|
||||
Waiting_RX = 0
|
||||
end
|
||||
end
|
||||
|
||||
local function DSM_Menu(event)
|
||||
local Speed = 0
|
||||
if event == EVT_VIRTUAL_NEXT then
|
||||
if Menu.EditLine == nil then
|
||||
-- not changing a value
|
||||
if Menu.SelLine ~= nil then
|
||||
if Menu.SelLine < 7 then
|
||||
local num = Menu.SelLine
|
||||
for i = Menu.SelLine + 1, 6, 1 do
|
||||
if Line[i].Type ~= nil and Line[i].Next ~= nil then
|
||||
Menu.SelLine=i
|
||||
break
|
||||
end
|
||||
end
|
||||
if num == Menu.SelLine then
|
||||
if Menu.Next ~= 0 then -- Next
|
||||
Menu.SelLine = 7
|
||||
elseif Menu.Prev ~= 0 then -- Prev
|
||||
Menu.SelLine = 8
|
||||
end
|
||||
end
|
||||
elseif Menu.Prev ~= 0 then -- Prev
|
||||
Menu.SelLine = 8
|
||||
end
|
||||
end
|
||||
else -- need to inc the value
|
||||
Value_Add(1)
|
||||
end
|
||||
elseif event == EVT_VIRTUAL_PREV then
|
||||
if Menu.EditLine == nil then
|
||||
if Menu.SelLine ~= nil then
|
||||
if Menu.SelLine == 8 and Menu.Next ~= 0 then
|
||||
Menu.SelLine = 7
|
||||
elseif Menu.SelLine > 0 then
|
||||
if Menu.SelLine > 6 then
|
||||
Menu.SelLine = 7
|
||||
end
|
||||
local num = Menu.SelLine
|
||||
for i = Menu.SelLine-1, 0, -1 do
|
||||
if Line[i].Type ~= nil and Line[i].Next ~= nil then
|
||||
Menu.SelLine=i
|
||||
break
|
||||
end
|
||||
end
|
||||
if num == Menu.SelLine then -- Back
|
||||
Menu.SelLine = -1
|
||||
end
|
||||
else
|
||||
Menu.SelLine = -1 -- Back
|
||||
end
|
||||
end
|
||||
else -- need to dec the value
|
||||
Value_Add(-1)
|
||||
end
|
||||
elseif event == EVT_VIRTUAL_ENTER then
|
||||
if Menu.SelLine == -1 then -- Back
|
||||
Menu.Cur = Menu.Back
|
||||
Menu.Id = Menu.BackId
|
||||
Menu.SelLine = 0
|
||||
Phase = MENU_TITLE
|
||||
Waiting_RX = 0
|
||||
elseif Menu.SelLine == 7 then -- Next
|
||||
Menu.Cur = Menu.Next
|
||||
Menu.Id = Menu.NextId
|
||||
Menu.SelLine = 0
|
||||
Phase = MENU_TITLE
|
||||
Waiting_RX = 0
|
||||
elseif Menu.SelLine == 8 then -- Prev
|
||||
Menu.Cur = Menu.Prev
|
||||
Menu.Id = Menu.PrevId
|
||||
Menu.SelLine = 0
|
||||
Phase = MENU_TITLE
|
||||
Waiting_RX = 0
|
||||
elseif Menu.SelLine ~= nil and Line[Menu.SelLine].Next ~= nil then
|
||||
if Line[Menu.SelLine].Type == MENU then -- Next menu exist
|
||||
Menu.Cur = Line[Menu.SelLine].Next
|
||||
Menu.Id = Line[Menu.SelLine].NextId
|
||||
Phase = MENU_TITLE
|
||||
Waiting_RX = 0
|
||||
else
|
||||
-- value entry
|
||||
if Menu.EditLine == Menu.SelLine then
|
||||
Menu.EditLine = nil
|
||||
Value_Changed = 0
|
||||
Phase = VALUE_CHANGED
|
||||
Waiting_RX = 0
|
||||
else
|
||||
Menu.EditLine = Menu.SelLine
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DSM_Send_Receive()
|
||||
if Waiting_RX == 0 then
|
||||
Waiting_RX = 1
|
||||
-- Need to send a request
|
||||
if Phase == RX_VERSION then -- request RX version
|
||||
DSM_Send(0x11,0x06,0x00,0x14,0x00,0x00)
|
||||
elseif Phase == WAIT_CMD then -- keep connection open
|
||||
DSM_Send(0x00,0x04,0x00,0x00)
|
||||
elseif Phase == MENU_TITLE then -- request menu title
|
||||
if Menu.Cur == nil then
|
||||
DSM_Send(0x12,0x06,0x00,0x14,0x00,0x00) -- first menu only
|
||||
Menu.Cur = 0
|
||||
else
|
||||
DSM_Send(0x16,0x06,Menu.Id,Menu.Cur,0x00,Menu.SelLine)
|
||||
end
|
||||
elseif Phase == MENU_LINES then -- request menu lines
|
||||
if Menu.CurLine == nil then
|
||||
DSM_Send(0x13,0x04,Menu.Id,Menu.Cur) -- line 0
|
||||
elseif Menu.CurLine >= 0x80 then
|
||||
local last_byte={0x40,0x01,0x02,0x04,0x00,0x00} -- unknown...
|
||||
DSM_Send(0x20,0x06,Menu.CurLine-0x80,Menu.CurLine-0x80,0x00,last_byte[Menu.CurLine-0x80+1]) -- line X
|
||||
else
|
||||
DSM_Send(0x14,0x06,Menu.Id,Menu.Cur,0x00,Menu.CurLine) -- line X
|
||||
end
|
||||
elseif Phase == MENU_VALUES then -- request menu values
|
||||
DSM_Send(0x15,0x06,Menu.Id,Menu.Cur,Line[Menu.CurLine].ValId,Menu.CurLine) -- line X
|
||||
elseif Phase == VALUE_CHANGING then -- send value
|
||||
local value=Line[Menu.SelLine].Val
|
||||
if value < 0 then
|
||||
value = 0x10000 + value
|
||||
end
|
||||
DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value
|
||||
Phase = VALUE_CHANGING_WAIT
|
||||
elseif Phase == VALUE_CHANGED then -- send value
|
||||
if Value_Changed == 0 then
|
||||
local value=Line[Menu.SelLine].Val
|
||||
if value < 0 then
|
||||
value = 0x10000 + value
|
||||
end
|
||||
DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value
|
||||
Value_Changed = Value_Changed + 1
|
||||
Waiting_RX = 0
|
||||
elseif Value_Changed == 1 then
|
||||
DSM_Send(0x19,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) -- validate
|
||||
-- Value_Changed = Value_Changed + 1
|
||||
-- Waiting_RX = 0
|
||||
--elseif Value_Changed == 2 then
|
||||
-- DSM_Send(0x1B,0x06,0x10,Menu.SelLine) -- validate again?
|
||||
-- Value_Changed = Value_Changed + 1
|
||||
end
|
||||
elseif Phase == VALUE_CHANGING_WAIT then
|
||||
DSM_Send(0x1A,0x06,Line[Menu.SelLine].ValId,Menu.SelLine)
|
||||
end
|
||||
multiBuffer(10,0x00);
|
||||
Retry = 50
|
||||
elseif multiBuffer(10) == 0x09 then
|
||||
-- Answer received
|
||||
--if multiBuffer(11) == 0x00 then -- waiting for commands?
|
||||
if multiBuffer(11) == 0x01 then -- read version
|
||||
--ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
RX.Name = Get_Text(multiBuffer(13))
|
||||
RX.Version = multiBuffer(14).."."..multiBuffer(15).."."..multiBuffer(16)
|
||||
Phase = MENU_TITLE
|
||||
elseif multiBuffer(11) == 0x02 then -- read menu title
|
||||
--ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00
|
||||
Menu.Cur = multiBuffer(12)
|
||||
Menu.Id = multiBuffer(13)
|
||||
Menu.Title = Get_Text(multiBuffer(14)+multiBuffer(15)*256)
|
||||
Menu.Prev = multiBuffer(16)
|
||||
Menu.PrevId = multiBuffer(17)
|
||||
Menu.Next = multiBuffer(18)
|
||||
Menu.NextId = multiBuffer(19)
|
||||
Menu.Back = multiBuffer(20)
|
||||
Menu.BackId = multiBuffer(21)
|
||||
for i = 0, 6 do -- clear menu
|
||||
Line[i] = { Menu = nil, Id = nil, Type = nil, Text="", Next = nil, NextId = nil, ValLine = nil, ValId = nil, Min, Max, Def, Val, Unit, Step }
|
||||
end
|
||||
Menu.CurLine = nil
|
||||
if Menu.Next ~= 0 then
|
||||
Menu.SelLine = 7 -- highlight Next
|
||||
else
|
||||
Menu.SelLine = -1 -- highlight Back
|
||||
end
|
||||
Blink = 0
|
||||
Phase = MENU_LINES
|
||||
elseif multiBuffer(11) == 0x03 then -- read menu lines
|
||||
--ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00
|
||||
-- Menu Id line Type Text_idx Next V_Id Val_Min Val_Max Val_Def
|
||||
--ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00
|
||||
Menu.CurLine = multiBuffer(14)
|
||||
local line = Line[Menu.CurLine]
|
||||
line.Menu = multiBuffer(12)
|
||||
line.Id = multiBuffer(13) -- not quite sure yet
|
||||
line.Type = multiBuffer(15) -- not quite sure yet: 1C is text menu only, 4C/6C is text followed by text list, C0 is text followed by percentage value
|
||||
line.Text = Get_Text(multiBuffer(16)+multiBuffer(17)*256)
|
||||
if multiBuffer(18) == Menu.Cur then
|
||||
line.Next = nil
|
||||
else
|
||||
line.Next = multiBuffer(18) -- not quite sure yet: 1C=text menu=>next menu, others=>line number of the value
|
||||
end
|
||||
if Menu.SelLine == -1 and line.Next ~= nil then -- Auto select first line of the menu
|
||||
Menu.SelLine = Menu.CurLine
|
||||
end
|
||||
line.NextId = multiBuffer(19) -- not quite sure yet
|
||||
line.ValLine = multiBuffer(18) -- not quite sure yet
|
||||
line.ValId = multiBuffer(19) -- not quite sure yet
|
||||
line.Min = conv_int16(multiBuffer(20)+multiBuffer(21)*256)
|
||||
line.Max = conv_int16(multiBuffer(22)+multiBuffer(23)*256)
|
||||
line.Def = conv_int16(multiBuffer(24)+multiBuffer(25)*256)
|
||||
if line.Type == MENU then
|
||||
-- nothing to do on menu entries
|
||||
elseif line.Type == LIST_MENU_NOCHANGING or line.Type == LIST_MENU2 then
|
||||
line.Val = nil --line.Def - line.Min -- use default value not sure if needed
|
||||
line.Def = line.Min -- pointer to the start of the list in Text
|
||||
line.Max = line.Max - line.Min -- max index
|
||||
line.Min = 0 -- min index
|
||||
else -- default to numerical value
|
||||
line.Val = nil --line.Def -- use default value not sure if needed
|
||||
end
|
||||
if line.Type ~= 0x1C then -- value to follow
|
||||
line.Text = line.Text..":"
|
||||
end
|
||||
Phase = MENU_LINES
|
||||
elseif multiBuffer(11) == 0x04 then -- read menu values
|
||||
--ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
-- Menu MeId line VaId Value
|
||||
--ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
Menu.CurLine = multiBuffer(14)
|
||||
Line[Menu.CurLine].Val = conv_int16(multiBuffer(16)+multiBuffer(17)*256)
|
||||
Phase = MENU_VALUES
|
||||
elseif multiBuffer(11) == 0x05 then -- unknown... need to get through the lines...
|
||||
Menu.CurLine = 0x80 + multiBuffer(12)
|
||||
Phase = MENU_LINES
|
||||
elseif multiBuffer(11) == 0x00 and Phase == VALUE_CHANGING then
|
||||
Phase = VALUE_CHANGING_WAIT
|
||||
end
|
||||
-- Data processed
|
||||
Waiting_RX = 0
|
||||
multiBuffer(10,0x00)
|
||||
Retry = 50
|
||||
else
|
||||
Retry = Retry - 1
|
||||
if Retry <= 0 then
|
||||
-- Retry the RX request
|
||||
Retry = 50
|
||||
Waiting_RX = 0
|
||||
if Phase ~= RX_VERSION and Phase ~= VALUE_CHANGING_WAIT then
|
||||
Phase = WAIT_CMD
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DSM_Display()
|
||||
lcd.clear()
|
||||
if LCD_W == 480 then
|
||||
--Draw title
|
||||
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
|
||||
lcd.drawText(1, 5, "DSM Forward Programming", MENU_TITLE_COLOR)
|
||||
--Draw RX Menu
|
||||
if Phase == RX_VERSION then
|
||||
lcd.drawText(10,50,"No compatible DSM RX...", BLINK)
|
||||
else
|
||||
if Menu.Title ~= nil then
|
||||
local attrib=0;
|
||||
lcd.drawText(80,32,Menu.Title,MIDSIZE)
|
||||
for i = 0, 6 do
|
||||
if i == Menu.SelLine then
|
||||
attrib = INVERS
|
||||
else
|
||||
attrib = 0
|
||||
end
|
||||
if Line[i] ~= nil and Line[i].Type ~= nil then
|
||||
if Line[i].Type ~= MENU then -- list/value
|
||||
if Line[i].Val ~= nil then
|
||||
local text=""
|
||||
if Line[i].Type == LIST_MENU_NOCHANGING or Line[i].Type == LIST_MENU2 then
|
||||
text = Get_Text(Line[i].Val+Line[i].Def)
|
||||
elseif Line[i].Type == PERCENTAGE_VALUE then
|
||||
text = Line[i].Val.." %"
|
||||
else
|
||||
text = Line[i].Val
|
||||
end
|
||||
if Menu.EditLine == Menu.SelLine then -- blink edited entry
|
||||
Blink = Blink + 1
|
||||
if Blink > 25 then
|
||||
attrib = 0
|
||||
if Blink > 50 then
|
||||
Blink = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
lcd.drawText(240,32+20*(i+2), text, attrib) -- display value
|
||||
end
|
||||
attrib = 0
|
||||
end
|
||||
lcd.drawText(10,32+20*(i+2), Line[i].Text, attrib) -- display text
|
||||
end
|
||||
end
|
||||
if Menu.SelLine == -1 then
|
||||
lcd.drawText(437,32, "Back", INVERS)
|
||||
else
|
||||
lcd.drawText(437,32, "Back", 0)
|
||||
end
|
||||
lcd.drawRectangle(437-5, 32-2, 47, 25)
|
||||
if Menu.Next ~= 0 then
|
||||
if Menu.SelLine == 7 then
|
||||
lcd.drawText(437,220, "Next",INVERS)
|
||||
else
|
||||
lcd.drawText(437,220, "Next")
|
||||
end
|
||||
lcd.drawRectangle(437-5, 220-2, 47, 25)
|
||||
end
|
||||
if Menu.Prev ~= 0 then
|
||||
if Menu.SelLine == 8 then
|
||||
lcd.drawText(5,220, "Prev",INVERS)
|
||||
else
|
||||
lcd.drawText(5,220, "Prev")
|
||||
end
|
||||
lcd.drawRectangle(5-5, 220-2, 47, 25)
|
||||
end
|
||||
end
|
||||
lcd.drawText(170,252, "RX "..RX.Name.." v"..RX.Version) -- display RX info
|
||||
end
|
||||
else
|
||||
-- --Draw RX Menu on LCD_W=128
|
||||
-- if multiBuffer( 4 ) == 0xFF then
|
||||
-- lcd.drawText(2,17,"No compatible DSM RX...",SMLSIZE)
|
||||
-- else
|
||||
-- if Retry_128 ~= 0 then
|
||||
-- --Intro page
|
||||
-- Retry_128 = Retry_128 - 1
|
||||
-- lcd.drawScreenTitle("DSM Forward Programming",0,0)
|
||||
-- lcd.drawText(2,17,"Press Prev Page for previous Menu" ,SMLSIZE)
|
||||
-- else
|
||||
-- --Menu page
|
||||
-- for line = 0, 7, 1 do
|
||||
-- for i = 0, 21-1, 1 do
|
||||
-- value=multiBuffer( line*21+6+i )
|
||||
-- if value > 0x80 then
|
||||
-- value = value - 0x80
|
||||
-- lcd.drawText(2+i*6,1+8*line,string.char(value).." ",SMLSIZE+INVERS)
|
||||
-- else
|
||||
-- lcd.drawText(2+i*6,1+8*line,string.char(value),SMLSIZE)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
||||
-- Init
|
||||
local function DSM_Init()
|
||||
--Set protocol to talk to
|
||||
multiBuffer( 0, string.byte('D') )
|
||||
--test if value has been written
|
||||
if multiBuffer( 0 ) ~= string.byte('D') then
|
||||
error("Not enough memory!")
|
||||
return 2
|
||||
end
|
||||
--Init TX buffer
|
||||
multiBuffer( 3, 0x00 )
|
||||
--Init RX buffer
|
||||
multiBuffer( 10, 0x00 )
|
||||
--Init telemetry
|
||||
multiBuffer( 0, string.byte('D') )
|
||||
multiBuffer( 1, string.byte('S') )
|
||||
multiBuffer( 2, string.byte('M') )
|
||||
|
||||
--Text to be displayed -> need to use a file instead?
|
||||
--RX names--
|
||||
Text[0x0014]="SPM4651T"
|
||||
Text[0x0015]="AR637T"
|
||||
--Lists--
|
||||
Text[0x0036]="Throttle"
|
||||
Text[0x0037]="Aileron"
|
||||
Text[0x0038]="Elevator"
|
||||
Text[0x0039]="Rudder"
|
||||
Text[0x003A]="Gear"
|
||||
|
||||
--******
|
||||
--This part is strange since the AR637T needs
|
||||
for i=1,7 do -- 3B..41
|
||||
Text[0x003A+i]="Aux"..i
|
||||
end
|
||||
for i=1,8 do -- 41..49
|
||||
Text[0x0041+i]="XPlus-"..i
|
||||
end
|
||||
--But FOTO-PETE reports that it should be:
|
||||
Text[0x0040]="Roll"
|
||||
Text[0x0041]="Pitch"
|
||||
Text[0x0042]="Yaw"
|
||||
Text[0x0046]="Priority"
|
||||
--******
|
||||
|
||||
Text[0x004A]="Failsafe"
|
||||
Text[0x004B]="Main Menu"
|
||||
Text[0x004E]="Position"
|
||||
Text[0x0050]="Outputs"
|
||||
Text[0x0051]="Output Channel 1"
|
||||
Text[0x0052]="Output Channel 2"
|
||||
Text[0x0053]="Output Channel 3"
|
||||
Text[0x0054]="Output Channel 4"
|
||||
Text[0x0055]="Output Channel 5"
|
||||
Text[0x0056]="Output Channel 6"
|
||||
--Text[0x005E]="Inhibit"
|
||||
Text[0x005F]="Hold Last"
|
||||
Text[0x0060]="Preset"
|
||||
--Text[0x0061]="Custom"
|
||||
--Messages--
|
||||
Text[0x0078]="FM Channel"
|
||||
Text[0x0080]="Orientation"
|
||||
Text[0x0085]="Frame Rate"
|
||||
Text[0x0086]="System Setup"
|
||||
Text[0x0087]="F-Mode Setup"
|
||||
Text[0x0088]="Enabled F-Modes"
|
||||
Text[0x008A]="Gain Sensitivity"
|
||||
Text[0x0090]="Apply"
|
||||
Text[0x0093]="Complete"
|
||||
Text[0x0094]="Done"
|
||||
Text[0x0097]="Factory Reset"
|
||||
Text[0x009A]="Capture Failsafe Positions"
|
||||
Text[0x009C]="Custom Failsafe"
|
||||
Text[0x00A5]="First Time Setup"
|
||||
Text[0x00AD]="Gain Channel Select"
|
||||
Text[0x00B6]="FM1"
|
||||
Text[0x00B7]="FM2"
|
||||
Text[0x00B8]="FM3"
|
||||
Text[0x00B9]="FM4"
|
||||
Text[0x00BA]="FM5"
|
||||
Text[0x00BB]="FM6"
|
||||
Text[0x00BC]="FM7"
|
||||
Text[0x00BD]="FM8"
|
||||
Text[0x00BE]="FM9"
|
||||
Text[0x00BF]="FM10"
|
||||
Text[0x00F9]="Gyro settings"
|
||||
Text[0x00FE]="Stick Priority"
|
||||
Text[0x0100]="Make sure the model has been"
|
||||
Text[0x0101]="configured, including wing type,"
|
||||
Text[0x0102]="reversing, travel, trimmed, etc."
|
||||
Text[0x0103]="before continuing setup."
|
||||
Text[0x0104]="0104"
|
||||
Text[0x0105]="0105"
|
||||
Text[0x0106]="Any wing type, channel assignment,"
|
||||
Text[0x0107]="subtrim, or servo reversing changes"
|
||||
Text[0x0108]="require running through initial"
|
||||
Text[0x0109]="setup again."
|
||||
Text[0x010A]="010A"
|
||||
Text[0x010B]="010B"
|
||||
Text[0x0190]="Relearn Servo Settings"
|
||||
Text[0x019C]="Enter Receiver Bind Mode"
|
||||
Text[0x01DC]="AS3X"
|
||||
Text[0x01DD]="AS3X Settings"
|
||||
Text[0x01DE]="AS3X Gains"
|
||||
Text[0x01E0]="Rate Gains"
|
||||
Text[0x020A]="Restore from Backup"
|
||||
Text[0x0209]="Save to Backup"
|
||||
Text[0x020D]="First Time SAFE Setup"
|
||||
Text[0x021A]="Set the model level,"
|
||||
Text[0x021B]="and press Continue."
|
||||
Text[0x021C]="021C"
|
||||
Text[0x021D]="021D"
|
||||
|
||||
Text[0x021F]="Set the model on its nose,"
|
||||
Text[0x0220]="and press Continue. If the"
|
||||
Text[0x0221]="orientation on the next"
|
||||
Text[0x0222]="screen is wrong go back"
|
||||
Text[0x0223]="and try again."
|
||||
Text[0x0224]="Continue"
|
||||
Text[0x0229]="Set Orientation Manually"
|
||||
|
||||
Text[0x0227]="Other settings"
|
||||
Text[0x022B]="WARNING!"
|
||||
Text[0x022C]="This will reset the"
|
||||
Text[0x022D]="configuration to factory"
|
||||
Text[0x022E]="defaults. This does not"
|
||||
Text[0x022F]="affect the backup config."
|
||||
Text[0x0230]="0230"
|
||||
Text[0x0231]="This will overwrite the"
|
||||
Text[0x0232]="backup memory with your"
|
||||
Text[0x0233]="current configuartion."
|
||||
Text[0x0234]="0234"
|
||||
Text[0x0235]="0235"
|
||||
Text[0x0236]="This will overwrite the"
|
||||
Text[0x0237]="current config with"
|
||||
Text[0x0238]="that which is in"
|
||||
Text[0x0239]="the backup memory."
|
||||
Text[0x023A]="023A"
|
||||
Text[0x023D]="Copy Flight Mode Settings"
|
||||
Text[0x0240]="Utilities"
|
||||
Text[0x8001]="Flight Mode 1"
|
||||
Text[0x8002]="Flight Mode 2"
|
||||
Text[0x8003]="Flight Mode 3"
|
||||
end
|
||||
|
||||
-- Main
|
||||
local function DSM_Run(event)
|
||||
if event == nil then
|
||||
error("Cannot be run as a model script!")
|
||||
return 2
|
||||
elseif event == EVT_VIRTUAL_EXIT then
|
||||
DSM_Release()
|
||||
return 2
|
||||
else
|
||||
DSM_Menu(event)
|
||||
DSM_Send_Receive()
|
||||
DSM_Display()
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
return { init=DSM_Init, run=DSM_Run }
|
||||
171
Lua_scripts/Graupner HoTT.lua
Normal file
171
Lua_scripts/Graupner HoTT.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
---- #########################################################################
|
||||
---- # #
|
||||
---- # Copyright (C) OpenTX #
|
||||
-----# #
|
||||
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
||||
---- # #
|
||||
---- # This program is free software; you can redistribute it and/or modify #
|
||||
---- # it under the terms of the GNU General Public License version 2 as #
|
||||
---- # published by the Free Software Foundation. #
|
||||
---- # #
|
||||
---- # This program is distributed in the hope that it will be useful #
|
||||
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
---- # GNU General Public License for more details. #
|
||||
---- # #
|
||||
---- #########################################################################
|
||||
|
||||
|
||||
--###############################################################################
|
||||
-- Multi buffer for HoTT description
|
||||
-- To start operation:
|
||||
-- Write "HoTT" at address 0..3
|
||||
-- Write 0xFF at address 4 will request the buffer to be cleared
|
||||
-- Write 0x0F at address 5
|
||||
-- Read buffer from address 6 access the RX text for 168 bytes, 21 caracters
|
||||
-- by 8 lines
|
||||
-- Write at address 5 sends an order to the RX: 0xXF=start, 0xX7=prev page,
|
||||
-- 0xXE=next page, 0xX9=enter, 0xXD=next or 0xXB=prev with X being the sensor
|
||||
-- to request data from 8=RX only, 9=Vario, A=GPS, B=Cust, C=ESC, D=GAM, E=EAM
|
||||
-- Write at address 4 the value 0xFF will request the buffer to be cleared
|
||||
-- !! Before exiting the script must write 0 at address 0 for normal operation !!
|
||||
--###############################################################################
|
||||
|
||||
HoTT_Sensor = 0
|
||||
Timer_128 = 100
|
||||
|
||||
local function HoTT_Release()
|
||||
multiBuffer( 0, 0 )
|
||||
end
|
||||
|
||||
local function HoTT_Send(button)
|
||||
multiBuffer( 5, 0x80+(HoTT_Sensor*16) + button)
|
||||
end
|
||||
|
||||
local function HoTT_Sensor_Inc()
|
||||
local detected_sensors=multiBuffer( 4 )
|
||||
local a
|
||||
if detected_sensors ~= 0xFF then
|
||||
repeat
|
||||
HoTT_Sensor=(HoTT_Sensor+1)%7 -- Switch to next sensor
|
||||
if HoTT_Sensor ~= 0 then
|
||||
a = math.floor(detected_sensors/ (2^(HoTT_Sensor-1))) -- shift right
|
||||
end
|
||||
until HoTT_Sensor==0 or a % 2 == 1
|
||||
HoTT_Send( 0x0F )
|
||||
end
|
||||
end
|
||||
|
||||
local function HoTT_Draw_LCD()
|
||||
local i
|
||||
local value
|
||||
local line
|
||||
local result
|
||||
local offset=0
|
||||
|
||||
local sensor_name = { "", "+Vario", "+GPS", "+Cust", "+ESC", "+GAM", "+EAM" }
|
||||
|
||||
lcd.clear()
|
||||
|
||||
if LCD_W == 480 then
|
||||
--Draw title
|
||||
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
|
||||
lcd.drawText(1, 5, "Graupner HoTT: config RX" .. sensor_name[HoTT_Sensor+1] .. " - Menu cycle Sensors", MENU_TITLE_COLOR)
|
||||
--Draw RX Menu
|
||||
if multiBuffer( 4 ) == 0xFF then
|
||||
lcd.drawText(10,50,"No HoTT telemetry...", BLINK)
|
||||
else
|
||||
for line = 0, 7, 1 do
|
||||
for i = 0, 21-1, 1 do
|
||||
value=multiBuffer( line*21+6+i )
|
||||
if value > 0x80 then
|
||||
value = value - 0x80
|
||||
lcd.drawText(10+i*16,32+20*line,string.char(value).." ",INVERS)
|
||||
else
|
||||
lcd.drawText(10+i*16,32+20*line,string.char(value))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
--Draw RX Menu on LCD_W=128
|
||||
if multiBuffer( 4 ) == 0xFF then
|
||||
lcd.drawText(2,17,"No HoTT telemetry...",SMLSIZE)
|
||||
else
|
||||
if Timer_128 ~= 0 then
|
||||
--Intro page
|
||||
Timer_128 = Timer_128 - 1
|
||||
lcd.drawScreenTitle("Graupner Hott",0,0)
|
||||
lcd.drawText(2,17,"Configuration of RX" .. sensor_name[HoTT_Sensor+1] ,SMLSIZE)
|
||||
lcd.drawText(2,37,"Press menu to cycle Sensors" ,SMLSIZE)
|
||||
else
|
||||
--Menu page
|
||||
for line = 0, 7, 1 do
|
||||
for i = 0, 21-1, 1 do
|
||||
value=multiBuffer( line*21+6+i )
|
||||
if value > 0x80 then
|
||||
value = value - 0x80
|
||||
lcd.drawText(2+i*6,1+8*line,string.char(value).." ",SMLSIZE+INVERS)
|
||||
else
|
||||
lcd.drawText(2+i*6,1+8*line,string.char(value),SMLSIZE)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Init
|
||||
local function HoTT_Init()
|
||||
--Set protocol to talk to
|
||||
multiBuffer( 0, string.byte('H') )
|
||||
--test if value has been written
|
||||
if multiBuffer( 0 ) ~= string.byte('H') then
|
||||
error("Not enough memory!")
|
||||
return 2
|
||||
end
|
||||
multiBuffer( 1, string.byte('o') )
|
||||
multiBuffer( 2, string.byte('T') )
|
||||
multiBuffer( 3, string.byte('T') )
|
||||
--Request init of the RX buffer
|
||||
multiBuffer( 4, 0xFF )
|
||||
--Request RX to send the config menu
|
||||
HoTT_Send( 0x0F )
|
||||
HoTT_Sensor = 0;
|
||||
HoTT_Detected_Sensors=0;
|
||||
Timer_128 = 100
|
||||
end
|
||||
|
||||
-- Main
|
||||
local function HoTT_Run(event)
|
||||
if event == nil then
|
||||
error("Cannot be run as a model script!")
|
||||
return 2
|
||||
elseif event == EVT_VIRTUAL_EXIT then
|
||||
HoTT_Release()
|
||||
return 2
|
||||
else
|
||||
if event == EVT_VIRTUAL_PREV_PAGE then
|
||||
killEvents(event)
|
||||
HoTT_Send( 0x07 )
|
||||
elseif event == EVT_VIRTUAL_ENTER then
|
||||
HoTT_Send( 0x09 )
|
||||
elseif event == EVT_VIRTUAL_PREV then
|
||||
HoTT_Send( 0x0B )
|
||||
elseif event == EVT_VIRTUAL_NEXT then
|
||||
HoTT_Send( 0x0D )
|
||||
elseif event == EVT_VIRTUAL_NEXT_PAGE then
|
||||
HoTT_Send( 0x0E )
|
||||
elseif event == EVT_VIRTUAL_MENU then
|
||||
Timer_128 = 100
|
||||
HoTT_Sensor_Inc()
|
||||
else
|
||||
HoTT_Send( 0x0F )
|
||||
end
|
||||
HoTT_Draw_LCD()
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
return { init=HoTT_Init, run=HoTT_Run }
|
||||
192
Lua_scripts/MultiChan.txt
Normal file
192
Lua_scripts/MultiChan.txt
Normal file
@@ -0,0 +1,192 @@
|
||||
24,0,Assan,Std,0,CH5,CH6,CH7,CH8
|
||||
14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2
|
||||
14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates
|
||||
14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf
|
||||
14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
|
||||
14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
|
||||
14,5,Bayang,QX100,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
|
||||
59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video
|
||||
59,1,BayangRX,CPPM,1,AnAux1,AnAux2,Flip,RTH,Pict,Video
|
||||
41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED
|
||||
42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED
|
||||
42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol
|
||||
34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess
|
||||
13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess
|
||||
37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8
|
||||
37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8
|
||||
37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8
|
||||
12,0,CX10,Green,1,Flip,Rate
|
||||
12,1,CX10,Blue,1,Flip,Rate,Pict,Video
|
||||
12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess
|
||||
12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video
|
||||
12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip
|
||||
12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH
|
||||
7,0,Devo,8CH,0,CH5,CH6,CH7,CH8
|
||||
7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10
|
||||
7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
7,3,Devo,6CH,0,CH5,CH6
|
||||
7,4,Devo,7CH,0,CH5,CH6,CH7
|
||||
33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow
|
||||
6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
|
||||
6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
|
||||
6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
|
||||
6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
|
||||
6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
|
||||
70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
|
||||
45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH
|
||||
45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH
|
||||
16,0,ESKY,Std,0,Gyro,Pitch
|
||||
16,1,ESKY,ET4,0,Gyro,Pitch
|
||||
35,0,ESKY150,4CH,0
|
||||
35,1,ESKY150,7CH,0,FMode,Aux6,Aux7
|
||||
69,0,ESKY150V2,Std,0,CH5_RA,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8
|
||||
1,1,Flysky,V9x9,1,Flip,Light,Pict,Video
|
||||
1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL
|
||||
1,3,Flysky,V912,1,BtmBtn,TopBtn
|
||||
1,4,Flysky,CX20,0,CH5,CH6,CH7
|
||||
28,0,Flysky_AFHDS2A,PWM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
53,0,Height,5ch,0,Gear
|
||||
53,1,Height,8ch,0,Gear,Gyro,Flap,Light
|
||||
25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8
|
||||
3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8
|
||||
3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8
|
||||
67,0,FrSkyL,LR12,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
67,1,FrSkyL,LR12_6CH,0,CH5,CH6
|
||||
15,0,FrSkyX,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
15,1,FrSkyX,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
|
||||
15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8
|
||||
15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
15,5,FrSkyX,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
|
||||
64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8
|
||||
64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
64,5,FrSkyX2,D16Cloned_8CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8
|
||||
65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8
|
||||
55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
55,1,FrSkyRX,CloneTX,0
|
||||
55,2,FrSkyRX,EraseTX,0
|
||||
55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
58,0,FX816,P38,1
|
||||
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
|
||||
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
|
||||
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
|
||||
47,0,GD00x,V1,1,Trim,LED,Rate
|
||||
47,1,GD00x,V2,1,Trim,LED,Rate
|
||||
32,0,GW008,FY326,1,Flip
|
||||
36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1
|
||||
36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
|
||||
36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
|
||||
36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
|
||||
4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8
|
||||
4,1,Hisky,HK310,0,Aux
|
||||
39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9
|
||||
39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9
|
||||
39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9
|
||||
26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib
|
||||
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
|
||||
57,0,HoTT,Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
57,1,HoTT,No_Sync,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
|
||||
2,1,Hubsan,H301,0,RTH,Light,Stab,Video
|
||||
2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes
|
||||
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
|
||||
71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
|
||||
71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
|
||||
49,0,KF606,Std,1,Trim
|
||||
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
|
||||
9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
|
||||
73,0,Kyosho,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
|
||||
18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,3,MJXQ,H26D,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
|
||||
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess
|
||||
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess
|
||||
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess
|
||||
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess
|
||||
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
|
||||
17,5,MT99XX,A180,0,3D_6G
|
||||
17,6,MT99XX,Dragon,0,Mode,RTH
|
||||
44,0,NCC1701,Std,1,Warp
|
||||
77,0,OMP,M2,0,THold,IdleUp,6G_3D
|
||||
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
|
||||
60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8
|
||||
51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess
|
||||
66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train
|
||||
29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal
|
||||
29,1,Q2x2,Q242,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
|
||||
29,2,Q2x2,Q282,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
|
||||
31,0,Q303,Q303,1,AltHol,Flip,Pict,Video,HLess,RTH,Gimbal
|
||||
31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal
|
||||
31,2,Q303,CX10D,1,Arm,Flip
|
||||
31,3,Q303,CX10WD,1,Arm,Flip
|
||||
72,0,Q90C,Std,0,FMode,VTX+
|
||||
74,0,RadioLink,Surface,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
74,1,RadioLink,Air,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
74,2,RadioLink,DumboRC,0,CH5,CH6,CH7,CH8,FS_CH1,FS_CH2,FS_CH3,FS_CH4,FS_CH5,FS_CH6,FS_CH7,FS_CH8
|
||||
76,0,Realacc,R11,1,Flip,Light,Calib,HLess,RTH,UNK
|
||||
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
|
||||
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
|
||||
21,0,Futaba,SFHSS,0,CH5,CH6,CH7,CH8
|
||||
19,0,Shenqi,Cycle,1
|
||||
68,0,Skyartec,Std,0,CH5,CH6,CH7
|
||||
11,0,SLT,V1,0,Gear,Pitch
|
||||
11,1,SLT,V2,0,CH5,CH6,CH7,CH8
|
||||
11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib
|
||||
11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib
|
||||
11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict
|
||||
10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess
|
||||
10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess
|
||||
61,0,Tiger,Std,1,Flip,Light
|
||||
43,0,Traxxas,6519,0
|
||||
5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY
|
||||
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
|
||||
48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn
|
||||
48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn
|
||||
46,0,V911s,V911s,1,Calib
|
||||
46,1,V911s,E119,1,Calib
|
||||
22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9
|
||||
30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8
|
||||
30,1,WK2x01,WK2401,0
|
||||
30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro
|
||||
30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro
|
||||
30,4,WK2x01,W6HEL,0,Gear,Col,Gyro
|
||||
30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro
|
||||
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
|
||||
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
|
||||
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
|
||||
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
|
||||
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
|
||||
8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess
|
||||
8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess
|
||||
52,0,ZSX,280,1,Light
|
||||
78,0,M-Link,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
|
||||
79,0,WFLY2,RF20x,0,CH5,CH6,CH7,CH8,CH9,CH10
|
||||
80,0,E016Hv2,E016Hv2,1,TakLan,EmStop,Flip,Calib,HLess,RTH
|
||||
81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE
|
||||
82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe
|
||||
83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR
|
||||
84,0,JOYSWAY,Std,0
|
||||
311
Lua_scripts/MultiChannelsUpdater.lua
Normal file
311
Lua_scripts/MultiChannelsUpdater.lua
Normal file
@@ -0,0 +1,311 @@
|
||||
|
||||
local toolName = "TNS|Multi chan namer|TNE"
|
||||
|
||||
---- #########################################################################
|
||||
---- # #
|
||||
---- # Copyright (C) OpenTX #
|
||||
-----# #
|
||||
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
||||
---- # #
|
||||
---- # This program is free software; you can redistribute it and/or modify #
|
||||
---- # it under the terms of the GNU General Public License version 2 as #
|
||||
---- # published by the Free Software Foundation. #
|
||||
---- # #
|
||||
---- # This program is distributed in the hope that it will be useful #
|
||||
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
---- # GNU General Public License for more details. #
|
||||
---- # #
|
||||
---- #########################################################################
|
||||
|
||||
local protocol_name = ""
|
||||
local sub_protocol_name = ""
|
||||
local bind_ch = 0
|
||||
local module_conf = {}
|
||||
local module_pos = "Internal"
|
||||
local file_ok = 0
|
||||
local done = 0
|
||||
local protocol = 0
|
||||
local sub_protocol = 0
|
||||
local f_seek = 0
|
||||
local channel_names={}
|
||||
local num_search = "Searching"
|
||||
|
||||
local function drawScreenTitle(title)
|
||||
if LCD_W == 480 then
|
||||
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
|
||||
lcd.drawText(1, 5, title, MENU_TITLE_COLOR)
|
||||
else
|
||||
lcd.drawScreenTitle(title, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function bitand(a, b)
|
||||
local result = 0
|
||||
local bitval = 1
|
||||
while a > 0 and b > 0 do
|
||||
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
|
||||
result = result + bitval -- set the current bit
|
||||
end
|
||||
bitval = bitval * 2 -- shift left
|
||||
a = math.floor(a/2) -- shift right
|
||||
b = math.floor(b/2)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function Multi_Draw_LCD(event)
|
||||
local line = 0
|
||||
|
||||
lcd.clear()
|
||||
drawScreenTitle("Multi channels namer")
|
||||
|
||||
--Display settings
|
||||
local lcd_opt = 0
|
||||
if LCD_W == 480 then
|
||||
x_pos = 10
|
||||
y_pos = 32
|
||||
y_inc = 20
|
||||
else
|
||||
x_pos = 0
|
||||
y_pos = 9
|
||||
y_inc = 8
|
||||
lcd_opt = SMLSIZE
|
||||
end
|
||||
|
||||
--Multi Module detection
|
||||
if module_conf["Type"] ~= 6 then
|
||||
if LCD_W == 480 then
|
||||
lcd.drawText(10,50,"No Multi module configured...", BLINK)
|
||||
else
|
||||
--Draw on LCD_W=128
|
||||
lcd.drawText(2,17,"No Multi module configured...",SMLSIZE)
|
||||
end
|
||||
return
|
||||
else
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,module_pos .. " Multi detected.", lcd_opt)
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
--Channel order
|
||||
if (ch_order == -1) then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels order can't be read from Multi...", lcd_opt)
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
--Can't open file MultiChan.txt
|
||||
if file_ok == 0 then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Can't read MultiChan.txt file...", lcd_opt)
|
||||
return
|
||||
end
|
||||
|
||||
if ( protocol_name == "" or sub_protocol_name == "" ) and f_seek ~=-1 then
|
||||
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
|
||||
if f == nil then return end
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,num_search, lcd_opt)
|
||||
num_search = num_search .. "."
|
||||
if #num_search > 15 then
|
||||
num_search = string.sub(num_search,1,9)
|
||||
end
|
||||
local proto = 0
|
||||
local sub_proto = 0
|
||||
local proto_name = ""
|
||||
local sub_proto_name = ""
|
||||
local channels = ""
|
||||
local nbr_try = 0
|
||||
local nbr_car = 0
|
||||
repeat
|
||||
io.seek(f, f_seek)
|
||||
local data = io.read(f, 100) -- read 100 characters
|
||||
if #data ==0 then
|
||||
f_seek = -1 -- end of file
|
||||
break
|
||||
end
|
||||
proto, sub_proto, proto_name, sub_proto_name, bind_ch, channels = string.match(data,'(%d+),(%d),([%w-_ ]+),([%w-_ ]+),(%d)(.+)')
|
||||
if proto ~= nil and sub_proto ~= nil and protocol_name ~= nil and sub_protocol_name ~= nil and bind_ch ~= nil then
|
||||
if tonumber(proto) == protocol and tonumber(sub_proto) == sub_protocol then
|
||||
protocol_name = proto_name
|
||||
sub_protocol_name = sub_proto_name
|
||||
bind_ch = tonumber(bind_ch)
|
||||
if channels ~= nil then
|
||||
--extract channel names
|
||||
nbr_car = string.find(channels, "\r")
|
||||
if nbr_car == nil then nbr_car = string.find(channels, "\n") end
|
||||
if nbr_car ~= nil then
|
||||
channels = string.sub(channels,1,nbr_car-1)
|
||||
end
|
||||
local i = 5
|
||||
for k in string.gmatch(channels, ",([%w-_ ]+)") do
|
||||
channel_names[i] = k
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
f_seek = -1 -- protocol found
|
||||
break
|
||||
end
|
||||
end
|
||||
if f_seek ~= -1 then
|
||||
nbr_car = string.find(data, "\n")
|
||||
if nbr_car == nil then nbr_car = string.find(data, "\r") end
|
||||
if nbr_car == nil then
|
||||
f_seek = -1 -- end of file
|
||||
break
|
||||
end
|
||||
f_seek = f_seek + nbr_car -- seek to next line
|
||||
nbr_try = nbr_try + 1
|
||||
end
|
||||
until nbr_try > 20 or f_seek == -1
|
||||
io.close(f)
|
||||
end
|
||||
|
||||
if f_seek ~= -1 then
|
||||
return -- continue searching...
|
||||
end
|
||||
|
||||
--Protocol & Sub_protocol
|
||||
if protocol_name == "" or sub_protocol_name == "" then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Unknown protocol "..tostring(protocol).."/"..tostring(sub_protocol).." ...", lcd_opt)
|
||||
return
|
||||
elseif LCD_W > 128 then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name .. " / SubProtocol: " .. sub_protocol_name, lcd_opt)
|
||||
line = line + 1
|
||||
else
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name, lcd_opt)
|
||||
line = line + 1
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"SubProtocol: " .. sub_protocol_name, lcd_opt)
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
text1=""
|
||||
text2=""
|
||||
for i,v in ipairs(channel_names) do
|
||||
if i<=8 then
|
||||
if i==1 then
|
||||
text1 = v
|
||||
else
|
||||
text1=text1 .. "," .. v
|
||||
end
|
||||
else
|
||||
if i==9 then
|
||||
text2 = v
|
||||
else
|
||||
text2=text2 .. "," .. v
|
||||
end
|
||||
end
|
||||
end
|
||||
if LCD_W > 128 then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels: " .. text1, lcd_opt)
|
||||
line = line + 1
|
||||
if text2 ~= "" then
|
||||
lcd.drawText(x_pos*9, y_pos+y_inc*line,text2, lcd_opt)
|
||||
line = line + 1
|
||||
end
|
||||
end
|
||||
|
||||
if event ~= EVT_VIRTUAL_ENTER and done == 0 then
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"<ENT> Save", lcd_opt + INVERS + BLINK)
|
||||
return
|
||||
end
|
||||
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Setting channel names.", lcd_opt)
|
||||
line = line + 1
|
||||
local output, nbr
|
||||
if done == 0 then
|
||||
for i,v in ipairs(channel_names) do
|
||||
output = model.getOutput(i-1)
|
||||
output["name"] = v
|
||||
model.setOutput(i-1,output)
|
||||
nbr = i
|
||||
end
|
||||
for i = nbr, 15 do
|
||||
output = model.getOutput(i)
|
||||
output["name"] = "n-a"
|
||||
model.setOutput(i,output)
|
||||
end
|
||||
if bind_ch == 1 then
|
||||
output = model.getOutput(15)
|
||||
output["name"] = "BindCH"
|
||||
model.setOutput(15,output)
|
||||
end
|
||||
done = 1
|
||||
end
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line,"Done!", lcd_opt)
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
-- Init
|
||||
local function Multi_Init()
|
||||
module_conf = model.getModule(0)
|
||||
if module_conf["Type"] ~= 6 then
|
||||
module_pos = "External"
|
||||
module_conf = model.getModule(1)
|
||||
if module_conf["Type"] ~= 6 then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
protocol = module_conf["protocol"]
|
||||
sub_protocol = module_conf["subProtocol"]
|
||||
|
||||
--Exceptions on first 4 channels...
|
||||
local stick_names = { "Rud", "Ele", "Thr", "Ail" }
|
||||
if ( protocol == 4 and sub_protocol == 1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX
|
||||
stick_names[2] = "n-a"
|
||||
stick_names[4] = "n-a"
|
||||
elseif protocol == 43 then -- Traxxas
|
||||
stick_names[2] = "Aux4"
|
||||
stick_names[4] = "Aux3"
|
||||
elseif ( protocol == 48 and sub_protocol == 0 ) then -- V761 3CH
|
||||
stick_names[4] = "n-a"
|
||||
elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816
|
||||
stick_names[1] = "n-a"
|
||||
stick_names[2] = "n-a"
|
||||
end
|
||||
|
||||
--Determine fist 4 channels order
|
||||
local ch_order=module_conf["channelsOrder"]
|
||||
if (ch_order == -1) then
|
||||
channel_names[1] = stick_names[defaultChannel(0)+1]
|
||||
channel_names[2] = stick_names[defaultChannel(1)+1]
|
||||
channel_names[3] = stick_names[defaultChannel(2)+1]
|
||||
channel_names[4] = stick_names[defaultChannel(3)+1]
|
||||
else
|
||||
channel_names[bitand(ch_order,3)+1] = stick_names[4]
|
||||
ch_order = math.floor(ch_order/4)
|
||||
channel_names[bitand(ch_order,3)+1] = stick_names[2]
|
||||
ch_order = math.floor(ch_order/4)
|
||||
channel_names[bitand(ch_order,3)+1] = stick_names[3]
|
||||
ch_order = math.floor(ch_order/4)
|
||||
channel_names[bitand(ch_order,3)+1] = stick_names[1]
|
||||
end
|
||||
|
||||
--Exceptions on first 4 channels...
|
||||
if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) ) then -- Kyosho or RadioLink Surface
|
||||
channel_names[1] = "ST"
|
||||
channel_names[2] = "THR"
|
||||
channel_names[3] = "CH3"
|
||||
channel_names[4] = "CH4"
|
||||
end
|
||||
|
||||
--Check MultiChan.txt
|
||||
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
|
||||
if f == nil then return end
|
||||
file_ok = 1
|
||||
io.close(f)
|
||||
end
|
||||
|
||||
-- Main
|
||||
local function Multi_Run(event)
|
||||
if event == nil then
|
||||
error("Cannot be run as a model script!")
|
||||
return 2
|
||||
else
|
||||
Multi_Draw_LCD(event)
|
||||
if event == EVT_VIRTUAL_EXIT then
|
||||
return 2
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
return { init=Multi_Init, run=Multi_Run }
|
||||
221
Lua_scripts/MultiLOLI.lua
Normal file
221
Lua_scripts/MultiLOLI.lua
Normal file
@@ -0,0 +1,221 @@
|
||||
|
||||
local toolName = "TNS|Multi LOLI RX config|TNE"
|
||||
|
||||
---- #########################################################################
|
||||
---- # #
|
||||
---- # Copyright (C) OpenTX #
|
||||
-----# #
|
||||
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
|
||||
---- # #
|
||||
---- # This program is free software; you can redistribute it and/or modify #
|
||||
---- # it under the terms of the GNU General Public License version 2 as #
|
||||
---- # published by the Free Software Foundation. #
|
||||
---- # #
|
||||
---- # This program is distributed in the hope that it will be useful #
|
||||
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
---- # GNU General Public License for more details. #
|
||||
---- # #
|
||||
---- #########################################################################
|
||||
|
||||
local loli_nok = false
|
||||
local channels={ { 768, "PWM", 100, 102, "PPM", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH1
|
||||
{ 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH2
|
||||
{ -768, "Servo", 0, -2048, "Switch", -100 }, -- CH3
|
||||
{ -768, "Servo", 0, -2048, "Switch", -100 }, -- CH4
|
||||
{ 102, "SBUS", 50, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH5
|
||||
{ -768, "Servo", 0, -2048, "Switch", -100 }, -- CH6
|
||||
{ 768, "PWM", 100, -768, "Servo", 0, -2048, "Switch", -100 }, -- CH7
|
||||
{ -768, "Servo", 0, -2048, "Switch", -100 } } -- CH8
|
||||
|
||||
local sel = 1
|
||||
local edit = false
|
||||
|
||||
local blink = 0
|
||||
local BLINK_SPEED = 15
|
||||
|
||||
local function drawScreenTitle(title)
|
||||
if LCD_W == 480 then
|
||||
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
|
||||
lcd.drawText(1, 5, title, MENU_TITLE_COLOR)
|
||||
else
|
||||
lcd.drawScreenTitle(title, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function LOLI_Draw_LCD(event)
|
||||
local line = 0
|
||||
|
||||
lcd.clear()
|
||||
|
||||
--Display settings
|
||||
local lcd_opt = 0
|
||||
if LCD_W == 480 then
|
||||
drawScreenTitle("Multi - LOLI RX configuration tool")
|
||||
x_pos = 152
|
||||
x_inc = 90
|
||||
y_pos = 40
|
||||
y_inc = 20
|
||||
else
|
||||
x_pos = 5
|
||||
x_inc = 30
|
||||
y_pos = 1
|
||||
y_inc = 8
|
||||
lcd_opt = SMLSIZE
|
||||
end
|
||||
|
||||
--Multi Module detection
|
||||
if loli_nok then
|
||||
if LCD_W == 480 then
|
||||
lcd.drawText(10,50,"The LOLI protocol is not selected...", lcd_opt)
|
||||
else
|
||||
--Draw on LCD_W=128
|
||||
lcd.drawText(2,17,"LOLI protocol not selected...",SMLSIZE)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
--Display current config
|
||||
if LCD_W == 480 then
|
||||
line = line + 1
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line -2, "Channel", lcd_opt)
|
||||
lcd.drawText(x_pos+x_inc, y_pos+y_inc*line -2, "Function", lcd_opt)
|
||||
lcd.drawRectangle(x_pos-4, y_pos+y_inc*line -4 , 2*x_inc +2, 188)
|
||||
lcd.drawLine(x_pos-4, y_pos+y_inc*line +18, x_pos-4 +2*x_inc +1, y_pos+y_inc*line +18, SOLID, 0)
|
||||
lcd.drawLine(x_pos+x_inc -5, y_pos+y_inc*line -4, x_pos+x_inc -5, y_pos+y_inc*line -5 +188, SOLID, 0)
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
local out
|
||||
for i = 1, 8 do
|
||||
out = getValue("ch"..(i+8))
|
||||
lcd.drawText(x_pos, y_pos+y_inc*line, "CH"..i, lcd_opt)
|
||||
for j = 1, #channels[i], 3 do
|
||||
if out > channels[i][j] then
|
||||
if sel == i then
|
||||
invert = INVERS
|
||||
if edit == true then
|
||||
blink = blink + 1
|
||||
if blink > BLINK_SPEED then
|
||||
invert = 0
|
||||
if blink > BLINK_SPEED * 2 then
|
||||
blink = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
invert = 0
|
||||
end
|
||||
lcd.drawText(x_pos+x_inc, y_pos+y_inc*line, channels[i][j+1], lcd_opt + invert)
|
||||
break
|
||||
end
|
||||
end
|
||||
line = line + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function LOLI_Change_Value(dir)
|
||||
local pos = 0
|
||||
local out
|
||||
--look for the current position
|
||||
out = getValue("ch"..(sel+8))
|
||||
for j = 1, #channels[sel], 3 do
|
||||
if out > channels[sel][j] then
|
||||
pos = j
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
--decrement or increment
|
||||
if dir < 0 and pos > 1 then
|
||||
pos = pos - 3
|
||||
elseif dir > 0 and pos + 3 < #channels[sel] then
|
||||
pos = pos + 3
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
--delete all mixers for the selected channel
|
||||
local num_mix = model.getMixesCount(sel-1 +8)
|
||||
for i = 1, num_mix do
|
||||
model.deleteMix(sel-1 +8, 0);
|
||||
end
|
||||
|
||||
--create new mixer
|
||||
local source_max = getFieldInfo("cyc1")
|
||||
|
||||
local val = { name = channels[sel][pos+1],
|
||||
source = source_max.id - 1, -- MAX=100 on TX16S
|
||||
weight = channels[sel][pos+2],
|
||||
offset = 0,
|
||||
switch = 0,
|
||||
multiplex = 0,
|
||||
curveType = 0,
|
||||
curveValue = 0,
|
||||
flightModes = 0,
|
||||
carryTrim = false,
|
||||
mixWarn = 0,
|
||||
delayUp = 0,
|
||||
delayDown = 0,
|
||||
speedUp = 0,
|
||||
speedDown = 0 }
|
||||
model.insertMix(sel-1 +8, 0, val)
|
||||
end
|
||||
|
||||
local function LOLI_Menu(event)
|
||||
if event == EVT_VIRTUAL_NEXT then
|
||||
if edit == false then
|
||||
-- not changing a value
|
||||
if sel < 8 then
|
||||
sel = sel + 1
|
||||
end
|
||||
else
|
||||
-- need to inc the value
|
||||
LOLI_Change_Value(1)
|
||||
end
|
||||
elseif event == EVT_VIRTUAL_PREV then
|
||||
if edit == false then
|
||||
-- not changing a value
|
||||
if sel > 1 then
|
||||
sel = sel - 1
|
||||
end
|
||||
else
|
||||
-- need to dec the value
|
||||
LOLI_Change_Value(-1)
|
||||
end
|
||||
elseif event == EVT_VIRTUAL_ENTER then
|
||||
if edit == false then
|
||||
edit = true
|
||||
blink = BLINK_SPEED
|
||||
else
|
||||
edit = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Init
|
||||
local function LOLI_Init()
|
||||
local module_conf = model.getModule(0)
|
||||
if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then
|
||||
module_conf = model.getModule(1)
|
||||
if module_conf["Type"] ~= 6 or module_conf["protocol"] ~= 82 then
|
||||
loli_nok = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Main
|
||||
local function LOLI_Run(event)
|
||||
if event == nil then
|
||||
error("Cannot be run as a model script!")
|
||||
return 2
|
||||
elseif event == EVT_VIRTUAL_EXIT then
|
||||
return 2
|
||||
else
|
||||
LOLI_Menu(event)
|
||||
LOLI_Draw_LCD(event)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
return { init=LOLI_Init, run=LOLI_Run }
|
||||
56
Lua_scripts/README.md
Normal file
56
Lua_scripts/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Multiprotocol TX Module OpenTX LUA scripts
|
||||
<img align="right" width=300 src="../docs/images/multi.png" />
|
||||
|
||||
If you like this project and want to support further development please consider making a [donation](../docs/Donations.md).
|
||||
|
||||
<table cellspacing=0>
|
||||
<tr>
|
||||
<td align=center width=200><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol¤cy_code=EUR&amount=5&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="../docs/images/donate_button.png" border="0" name="submit" title="PayPal - Donate €5" alt="Donate €5"/></a><br><b>€5</b></td>
|
||||
<td align=center width=200><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol¤cy_code=EUR&amount=10&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="../docs/images/donate_button.png" border="0" name="submit" title="PayPal - Donate €10" alt="Donate €10"/></a><br><b>€10</b></td>
|
||||
<td align=center width=200><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol¤cy_code=EUR&amount=15&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="../docs/images/donate_button.png" border="0" name="submit" title="PayPal - Donate €15" alt="Donate €10"/></a><br><b>€15</b></td>
|
||||
<td align=center width=200><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol¤cy_code=EUR&amount=25&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="../docs/images/donate_button.png" border="0" name="submit" title="PayPal - Donate €25" alt="Donate €25"/></a><br><b>€25</b></td>
|
||||
<td align=center width=200><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="../docs/images/donate_button.png" border="0" name="submit" title="PayPal - Donate" alt="Donate"/></a><br><b>Other</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## MultiChannelsUpdater
|
||||
|
||||
Automatically name the channels based on the loaded Multi protocol and sub protocol including the module channel order convention.
|
||||
|
||||
Need OpenTX 2.3.9 or above. Located on the radio SD card under \SCRIPTS\TOOLS. This script needs MultiChan.txt to be present in the same folder.
|
||||
|
||||
[](https://www.youtube.com/watch?v=L58ayXuewyA)
|
||||
|
||||
## MultiLOLI
|
||||
|
||||
Script to set the channels function (switch, servo, pwm, ppm, sbus) on a [LOLI RX](https://github.com/pascallanger/DIY-Multiprotocol-TX-Module/blob/master/Protocols_Details.md#loli---82)
|
||||
|
||||
[](https://www.youtube.com/watch?v=e698pQxfv-A)
|
||||
|
||||
## Graupner HoTT
|
||||
|
||||
Enable text configuration of the HoTT RX and sensors: Vario, GPS, ESC, GAM and EAM.
|
||||
|
||||
Need OpenTX 2.3.9 or above. Located on the radio SD card under \SCRIPTS\TOOLS.
|
||||
|
||||
Notes:
|
||||
- Menu/MDL/Model is used to cycle through the detected sensors.
|
||||
- It's normal to lose the telemetry feed while using the text mode configuration. Telemetry will resume properly if the script is exited by doing a short press on the exit button.
|
||||
|
||||
[](https://www.youtube.com/watch?v=81wd8NlF3Qw)
|
||||
|
||||
## DSM Forward Programming
|
||||
|
||||
This is a work in progress. It's only available for color screens (Horus, TX16S, T16, T18...).
|
||||
|
||||
If some text appears as Unknown_xxx, please report xxx and what the exact text display should be.
|
||||
|
||||
Need OpenTX 2.3.10 nightly or above. Located on the radio SD card under \SCRIPTS\TOOLS.
|
||||
|
||||
[](https://www.youtube.com/watch?v=sjIaDw5j9nE)
|
||||
|
||||
## DSM PID Flight log gain parameters for Blade micros
|
||||
|
||||
Lua telemetry script from [feathering on RCGroups](https://www.rcgroups.com/forums/showpost.php?p=46033341&postcount=20728) to facilitate setting the Gain Parameters on the Blade 150S FBL. It doesn't use Forward Programming but instead it just reads telemetry data from the Multi-module and displays it on a telemetry display.
|
||||
|
||||
It is very similar to the Telemetry Based Text Generator functionality on Spektrum transmitters where one doesn't need to rely on the angle of the swashplate to determine selection/value.
|
||||
127
Lua_scripts/pidDsm.lua
Normal file
127
Lua_scripts/pidDsm.lua
Normal file
@@ -0,0 +1,127 @@
|
||||
--
|
||||
-- This telemetry script displays the Flight Log Gain
|
||||
-- Parameters streamed from the Blade 150S Spektrum AR6335A
|
||||
-- Flybarless Controller.
|
||||
|
||||
-- The script facilitates the setting of the FBL's
|
||||
-- Gain Parameters including PID for both
|
||||
-- cyclic and tail. It is similar to the Telemetry Based
|
||||
-- Text Generator available on Spektrum transmitters.
|
||||
|
||||
-- Supporting similar Blade micros such as the Fusion 180
|
||||
-- would possibly require minor modifications to this script.
|
||||
|
||||
-- This script reads telemetry data from the Spektrum
|
||||
-- receiver and thus functionality relies on data being
|
||||
-- captured by the OpenTX transmitter. A DSM
|
||||
-- telemetry-ready module is required. Please see the
|
||||
-- MULTI-Module project at https://www.multi-module.org/.
|
||||
|
||||
-- The only supported display is the Taranis'. It may work
|
||||
-- with higher res screens.
|
||||
--
|
||||
|
||||
|
||||
-- Sensor names
|
||||
local PSensor = "FdeA"
|
||||
local ISensor = "FdeB"
|
||||
local DSensor = "FdeL"
|
||||
local RSensor = "FdeR"
|
||||
local ActiveParamSensor = "Hold"
|
||||
|
||||
local tags = {"P", "I", "D"}
|
||||
|
||||
|
||||
local function getPage(iParam)
|
||||
-- get page from 0-based index
|
||||
-- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2)
|
||||
local res = (math.floor(iParam/4)==0) and 1 or 2
|
||||
return res
|
||||
end
|
||||
|
||||
function round(v)
|
||||
-- round float
|
||||
local factor = 100
|
||||
return math.floor(v * factor + 0.5) / factor
|
||||
end
|
||||
|
||||
|
||||
local function readValue(sensor)
|
||||
-- read from sensor, round and return
|
||||
local v = getValue(sensor)
|
||||
v = round(v)
|
||||
return v
|
||||
end
|
||||
|
||||
local function readActiveParamValue(sensor)
|
||||
-- read and return a validated active parameter value
|
||||
local v = getValue(sensor)
|
||||
if (v<1 or v>8) then
|
||||
return nil
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local function readParameters()
|
||||
-- read and return parameters
|
||||
local p = readValue(PSensor)
|
||||
local i = readValue(ISensor)
|
||||
local d = readValue(DSensor)
|
||||
local r = readValue(RSensor)
|
||||
local a = readActiveParamValue(ActiveParamSensor)
|
||||
return {p,i,d,r,a}
|
||||
end
|
||||
|
||||
local function drawParameters()
|
||||
-- draw labels and params on screen
|
||||
local params = readParameters()
|
||||
local activeParam = params[5]
|
||||
|
||||
-- if active gain does not validate then assume
|
||||
-- Gain Adjustment Mode is disabled
|
||||
if not activeParam then
|
||||
lcd.clear()
|
||||
lcd.drawText(20,30,"Please enter Gain Adjustment Mode")
|
||||
return
|
||||
end
|
||||
|
||||
local activePage = getPage(activeParam-1)
|
||||
for iParam=0,7 do
|
||||
-- highlight selected parameter
|
||||
local attr = (activeParam==iParam+1) and 2 or 0
|
||||
-- circular index (per page)
|
||||
local perPageIndx = iParam % 4 + 1
|
||||
-- check if displaying cyclic params.
|
||||
local isCyclicPage = (getPage(iParam)==1)
|
||||
-- set y draw coord
|
||||
local y = perPageIndx*10+2
|
||||
|
||||
-- labels
|
||||
local x = isCyclicPage and 6 or 120
|
||||
-- labels are P,I,D for both pages except for last param
|
||||
local val = iParam==3 and "Response" or
|
||||
(iParam==7 and "Filtering" or tags[perPageIndx])
|
||||
lcd.drawText (x, y, val, attr)
|
||||
|
||||
-- gains
|
||||
-- set all params for non-active page to '--' rather than 'last value'
|
||||
val = (getPage(iParam)==activePage) and params[perPageIndx] or '--'
|
||||
x = isCyclicPage and 70 or 180
|
||||
lcd.drawText (x, y, val, attr)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function run_func(event)
|
||||
-- TODO: calling clear() on every function call redrawing all labels is not ideal
|
||||
lcd.clear()
|
||||
lcd.drawText (8, 2, "Cyclic (0...200)")
|
||||
lcd.drawText (114, 2, "Tail (0...200)")
|
||||
drawParameters()
|
||||
end
|
||||
|
||||
local function init_func() end
|
||||
local function bg_func() end
|
||||
|
||||
|
||||
return { run=run_func, background=bg_func, init=init_func }
|
||||
@@ -12,101 +12,78 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//-------------------------------
|
||||
//-------------------------------
|
||||
//A7105 SPI routines
|
||||
//-------------------------------
|
||||
//-------------------------------
|
||||
/********************/
|
||||
/** A7105 routines **/
|
||||
/********************/
|
||||
#ifdef A7105_INSTALLED
|
||||
#include "iface_a7105.h"
|
||||
|
||||
void A7105_WriteData(uint8_t len, uint8_t channel)
|
||||
{
|
||||
uint8_t i;
|
||||
CS_off;
|
||||
A7105_Write(A7105_RST_WRPTR);
|
||||
A7105_Write(0x05);
|
||||
A7105_CSN_off;
|
||||
SPI_Write(A7105_RST_WRPTR);
|
||||
SPI_Write(A7105_05_FIFO_DATA);
|
||||
for (i = 0; i < len; i++)
|
||||
A7105_Write(packet[i]);
|
||||
CS_on;
|
||||
A7105_WriteReg(0x0F, channel);
|
||||
SPI_Write(packet[i]);
|
||||
A7105_CSN_on;
|
||||
if(protocol!=PROTO_WFLY2)
|
||||
{
|
||||
if(!(protocol==PROTO_FLYSKY || protocol==PROTO_KYOSHO))
|
||||
{
|
||||
A7105_Strobe(A7105_STANDBY); //Force standby mode, ie cancel any TX or RX...
|
||||
A7105_SetTxRxMode(TX_EN); //Switch to PA
|
||||
}
|
||||
A7105_WriteReg(A7105_0F_PLL_I, channel);
|
||||
A7105_Strobe(A7105_TX);
|
||||
}
|
||||
}
|
||||
|
||||
void A7105_ReadData() {
|
||||
void A7105_ReadData(uint8_t len)
|
||||
{
|
||||
uint8_t i;
|
||||
A7105_Strobe(0xF0); //A7105_RST_RDPTR
|
||||
CS_off;
|
||||
A7105_Write(0x45);
|
||||
for (i=0;i<16;i++)
|
||||
packet[i]=A7105_Read();
|
||||
CS_on;
|
||||
A7105_Strobe(A7105_RST_RDPTR);
|
||||
A7105_CSN_off;
|
||||
SPI_Write(0x40 | A7105_05_FIFO_DATA); //bit 6 =1 for reading
|
||||
for (i=0;i<len;i++)
|
||||
packet[i]=SPI_SDI_Read();
|
||||
A7105_CSN_on;
|
||||
}
|
||||
|
||||
void A7105_WriteReg(uint8_t address, uint8_t data) {
|
||||
CS_off;
|
||||
A7105_Write(address);
|
||||
A7105_CSN_off;
|
||||
SPI_Write(address);
|
||||
NOP();
|
||||
A7105_Write(data);
|
||||
CS_on;
|
||||
SPI_Write(data);
|
||||
A7105_CSN_on;
|
||||
}
|
||||
|
||||
uint8_t A7105_ReadReg(uint8_t address) {
|
||||
uint8_t A7105_ReadReg(uint8_t address)
|
||||
{
|
||||
uint8_t result;
|
||||
CS_off;
|
||||
A7105_Write(address |=0x40); //bit 6 =1 for reading
|
||||
result = A7105_Read();
|
||||
CS_on;
|
||||
A7105_CSN_off;
|
||||
SPI_Write(address |=0x40); //bit 6 =1 for reading
|
||||
result = SPI_SDI_Read();
|
||||
A7105_CSN_on;
|
||||
return(result);
|
||||
}
|
||||
|
||||
void A7105_Write(uint8_t command) {
|
||||
uint8_t n=8;
|
||||
|
||||
SCK_off;//SCK start low
|
||||
SDI_off;
|
||||
while(n--) {
|
||||
if(command&0x80)
|
||||
SDI_on;
|
||||
else
|
||||
SDI_off;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
command = command << 1;
|
||||
}
|
||||
SDI_on;
|
||||
}
|
||||
|
||||
uint8_t A7105_Read(void) {
|
||||
uint8_t result=0;
|
||||
uint8_t i;
|
||||
|
||||
SDI_SET_INPUT;
|
||||
for(i=0;i<8;i++) {
|
||||
if(SDI_1) ///if SDIO =1
|
||||
result=(result<<1)|0x01;
|
||||
else
|
||||
result=result<<1;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
NOP();
|
||||
}
|
||||
SDI_SET_OUTPUT;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------
|
||||
void A7105_SetTxRxMode(uint8_t mode)
|
||||
{
|
||||
if(mode == TX_EN) {
|
||||
if(mode == TX_EN)
|
||||
{
|
||||
A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x33);
|
||||
A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x31);
|
||||
} else if (mode == RX_EN) {
|
||||
}
|
||||
else
|
||||
if (mode == RX_EN)
|
||||
{
|
||||
A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x31);
|
||||
A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x33);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//The A7105 seems to some with a cross-wired power-amp (A7700)
|
||||
//On the XL7105-D03, TX_EN -> RXSW and RX_EN -> TXSW
|
||||
//This means that sleep mode is wired as RX_EN = 1 and TX_EN = 1
|
||||
@@ -121,38 +98,37 @@ uint8_t A7105_Reset()
|
||||
{
|
||||
uint8_t result;
|
||||
|
||||
delay(10); //wait 10ms for A7105 wakeup
|
||||
A7105_WriteReg(0x00, 0x00);
|
||||
delay(1000);
|
||||
A7105_WriteReg(A7105_00_MODE, 0x00);
|
||||
delayMilliseconds(1);
|
||||
A7105_SetTxRxMode(TXRX_OFF); //Set both GPIO as output and low
|
||||
result=A7105_ReadReg(0x10) == 0x9E; //check if is reset.
|
||||
result=A7105_ReadReg(A7105_10_PLL_II) == 0x9E; //check if is reset.
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
return result;
|
||||
}
|
||||
|
||||
void A7105_WriteID(uint32_t ida) {
|
||||
CS_off;
|
||||
A7105_Write(0x06);//ex id=0x5475c52a ;txid3txid2txid1txid0
|
||||
A7105_Write((ida>>24)&0xff);//53
|
||||
A7105_Write((ida>>16)&0xff);//75
|
||||
A7105_Write((ida>>8)&0xff);//c5
|
||||
A7105_Write((ida>>0)&0xff);//2a
|
||||
CS_on;
|
||||
void A7105_WriteID(uint32_t ida)
|
||||
{
|
||||
A7105_CSN_off;
|
||||
SPI_Write(A7105_06_ID_DATA); //ex id=0x5475c52a ;txid3txid2txid1txid0
|
||||
SPI_Write((ida>>24)&0xff); //54
|
||||
SPI_Write((ida>>16)&0xff); //75
|
||||
SPI_Write((ida>>8)&0xff); //c5
|
||||
SPI_Write((ida>>0)&0xff); //2a
|
||||
A7105_CSN_on;
|
||||
}
|
||||
|
||||
void A7105_SetPower_Value(int power)
|
||||
{
|
||||
/*
|
||||
Power amp is ~+16dBm so:
|
||||
TXPOWER_100uW = -23dBm == PAC=0 TBG=0
|
||||
TXPOWER_300uW = -20dBm == PAC=0 TBG=1
|
||||
TXPOWER_1mW = -16dBm == PAC=0 TBG=2
|
||||
TXPOWER_3mW = -11dBm == PAC=0 TBG=4
|
||||
TXPOWER_10mW = -6dBm == PAC=1 TBG=5
|
||||
TXPOWER_30mW = 0dBm == PAC=2 TBG=7
|
||||
TXPOWER_100mW = 1dBm == PAC=3 TBG=7
|
||||
TXPOWER_150mW = 1dBm == PAC=3 TBG=7
|
||||
*/
|
||||
static void A7105_SetPower_Value(int power)
|
||||
{
|
||||
//Power amp is ~+16dBm so:
|
||||
//TXPOWER_100uW = -23dBm == PAC=0 TBG=0
|
||||
//TXPOWER_300uW = -20dBm == PAC=0 TBG=1
|
||||
//TXPOWER_1mW = -16dBm == PAC=0 TBG=2
|
||||
//TXPOWER_3mW = -11dBm == PAC=0 TBG=4
|
||||
//TXPOWER_10mW = -6dBm == PAC=1 TBG=5
|
||||
//TXPOWER_30mW = 0dBm == PAC=2 TBG=7
|
||||
//TXPOWER_100mW = 1dBm == PAC=3 TBG=7
|
||||
//TXPOWER_150mW = 1dBm == PAC=3 TBG=7
|
||||
uint8_t pac, tbg;
|
||||
switch(power) {
|
||||
case 0: pac = 0; tbg = 0; break;
|
||||
@@ -167,62 +143,329 @@ void A7105_SetPower_Value(int power)
|
||||
};
|
||||
A7105_WriteReg(0x28, (pac << 3) | tbg);
|
||||
}
|
||||
*/
|
||||
|
||||
void A7105_SetPower()
|
||||
{
|
||||
uint8_t power=A7105_BIND_POWER;
|
||||
if(IS_BIND_DONE_on)
|
||||
if(IS_BIND_DONE)
|
||||
#ifdef A7105_ENABLE_LOW_POWER
|
||||
power=IS_POWER_FLAG_on?A7105_HIGH_POWER:A7105_LOW_POWER;
|
||||
#else
|
||||
power=A7105_HIGH_POWER;
|
||||
#endif
|
||||
if(IS_RANGE_FLAG_on)
|
||||
power=A7105_RANGE_POWER;
|
||||
A7105_WriteReg(0x28, power);
|
||||
if(prev_power != power)
|
||||
{
|
||||
A7105_WriteReg(A7105_28_TX_TEST, power);
|
||||
prev_power=power;
|
||||
}
|
||||
}
|
||||
|
||||
void A7105_Strobe(uint8_t address) {
|
||||
CS_off;
|
||||
A7105_Write(address);
|
||||
CS_on;
|
||||
A7105_CSN_off;
|
||||
SPI_Write(address);
|
||||
A7105_CSN_on;
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM HUBSAN_A7105_regs[] = {
|
||||
0xFF, 0x63, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x04, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0xFF, 0xFF, 0x62, 0x80, 0xFF, 0xFF, 0x0A, 0xFF, 0xFF, 0x07,
|
||||
0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x47, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF
|
||||
};
|
||||
const uint8_t PROGMEM FLYSKY_A7105_regs[] = {
|
||||
0xff, 0x42, 0x00, 0x14, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50,
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f,
|
||||
0x13, 0xc3, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00,
|
||||
0x01, 0x0f, 0xff
|
||||
};
|
||||
void A7105_Init(uint8_t protocol)
|
||||
// Fine tune A7105 LO base frequency
|
||||
// this is required for some A7105 modules and/or RXs with inaccurate crystal oscillator
|
||||
void A7105_AdjustLOBaseFreq(uint8_t cmd)
|
||||
{
|
||||
void *A7105_Regs;
|
||||
static int16_t old_offset=2048;
|
||||
int16_t offset=1024;
|
||||
if(cmd==0)
|
||||
{ // Called at init of the A7105
|
||||
old_offset=2048;
|
||||
switch(protocol)
|
||||
{
|
||||
case PROTO_HUBSAN:
|
||||
#ifdef FORCE_HUBSAN_TUNING
|
||||
offset=(int16_t)FORCE_HUBSAN_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_BUGS:
|
||||
#ifdef FORCE_BUGS_TUNING
|
||||
offset=(int16_t)FORCE_BUGS_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_FLYSKY:
|
||||
#ifdef FORCE_FLYSKY_TUNING
|
||||
offset=(int16_t)FORCE_FLYSKY_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_HEIGHT:
|
||||
#ifdef FORCE_HEIGHT_TUNING
|
||||
offset=(int16_t)FORCE_HEIGHT_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_PELIKAN:
|
||||
#ifdef FORCE_PELIKAN_TUNING
|
||||
offset=(int16_t)FORCE_PELIKAN_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_KYOSHO:
|
||||
#ifdef FORCE_KYOSHO_TUNING
|
||||
offset=(int16_t)FORCE_KYOSHO_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_JOYSWAY:
|
||||
#ifdef FORCE_JOYSWAY_TUNING
|
||||
offset=(int16_t)FORCE_JOYSWAY_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_WFLY2:
|
||||
#ifdef FORCE_WFLY2_TUNING
|
||||
offset=(int16_t)FORCE_WFLY2_TUNING;
|
||||
#endif
|
||||
break;
|
||||
case PROTO_AFHDS2A:
|
||||
case PROTO_AFHDS2A_RX:
|
||||
#ifdef FORCE_AFHDS2A_TUNING
|
||||
offset=(int16_t)FORCE_AFHDS2A_TUNING;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(offset==1024) // Use channel 15 as an input
|
||||
offset=convert_channel_16b_nolimit(CH15,-300,300,false);
|
||||
|
||||
if(protocol==INIT_FLYSKY)
|
||||
if(old_offset==offset) // offset is the same as before...
|
||||
return;
|
||||
old_offset=offset;
|
||||
|
||||
// LO base frequency = 32e6*(bip+(bfp/(2^16)))
|
||||
uint8_t bip; // LO base frequency integer part
|
||||
uint16_t bfp; // LO base frequency fractional part
|
||||
offset++; // as per datasheet, not sure why recommended, but that's a +1kHz drift only ...
|
||||
offset<<=1;
|
||||
if(offset < 0)
|
||||
{
|
||||
A7105_WriteID(0x5475c52A);//0x2Ac57554
|
||||
A7105_Regs=(void *)FLYSKY_A7105_regs;
|
||||
bip = 0x4a; // 2368 MHz
|
||||
bfp = 0xffff + offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
A7105_WriteID(0x55201041);
|
||||
A7105_Regs=(void *)HUBSAN_A7105_regs;
|
||||
bip = 0x4b; // 2400 MHz (default)
|
||||
bfp = offset;
|
||||
}
|
||||
for (uint8_t i = 0; i < 0x33; i++){
|
||||
if( pgm_read_byte_near((uint16_t)(A7105_Regs)+i) != 0xFF)
|
||||
A7105_WriteReg(i, pgm_read_byte_near((uint16_t)(A7105_Regs)+i));
|
||||
A7105_WriteReg( A7105_11_PLL_III, bip);
|
||||
A7105_WriteReg( A7105_12_PLL_IV, (bfp >> 8) & 0xff);
|
||||
A7105_WriteReg( A7105_13_PLL_V, bfp & 0xff);
|
||||
//debugln("Channel: %d, offset: %d, bip: %2x, bfp: %4x", Channel_data[14], offset, bip, bfp);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) A7105_SetVCOBand(uint8_t vb1, uint8_t vb2)
|
||||
{ // Set calibration band value to best match
|
||||
uint8_t diff1, diff2;
|
||||
|
||||
if (vb1 >= 4)
|
||||
diff1 = vb1 - 4;
|
||||
else
|
||||
diff1 = 4 - vb1;
|
||||
|
||||
if (vb2 >= 4)
|
||||
diff2 = vb2 - 4;
|
||||
else
|
||||
diff2 = 4 - vb2;
|
||||
|
||||
if (diff1 == diff2 || diff1 > diff2)
|
||||
A7105_WriteReg(A7105_25_VCO_SBCAL_I, vb1 | 0x08);
|
||||
else
|
||||
A7105_WriteReg(A7105_25_VCO_SBCAL_I, vb2 | 0x08);
|
||||
}
|
||||
|
||||
#if defined(AFHDS2A_A7105_INO) || defined(AFHDS2A_RX_A7105_INO)
|
||||
const uint8_t PROGMEM AFHDS2A_A7105_regs[] = {
|
||||
0xFF, 0x42 | (1<<5), 0x00, 0x25, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x05, 0x00, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x4f, 0x62, 0x80, 0xFF, 0xFF, 0x2a, 0x32, 0xc3, 0x1f, // 10 - 1f
|
||||
0x1e, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef BUGS_A7105_INO
|
||||
const uint8_t PROGMEM BUGS_A7105_regs[] = {
|
||||
0xFF, 0x42, 0x00, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0x01, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f, // 10 - 1f
|
||||
0x16, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3b, 0x00, 0x0b, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef FLYSKY_A7105_INO
|
||||
const uint8_t PROGMEM FLYSKY_A7105_regs[] = {
|
||||
0xff, 0x42, 0x00, 0x14, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f, // 10 - 1f
|
||||
0x13, 0xc3, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef HEIGHT_A7105_INO
|
||||
const uint8_t PROGMEM HEIGHT_A7105_regs[] = {
|
||||
0xff, 0x42, 0x00, 0x07, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x01, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x1f, // 10 - 1f
|
||||
0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3a, 0x00, 0x3f, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef HUBSAN_A7105_INO
|
||||
const uint8_t PROGMEM HUBSAN_A7105_regs[] = {
|
||||
0xFF, 0x63, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x04, 0xFF, // 00 - 0f
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0xFF, 0xFF, 0x62, 0x80, 0xFF, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, // 10 - 1f
|
||||
0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x47, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20 - 2f
|
||||
0xFF, 0xFF // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef PELIKAN_A7105_INO
|
||||
const uint8_t PROGMEM PELIKAN_A7105_regs[] = {
|
||||
0xff, 0x42, 0x00, 0x0F, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x01, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x07, // 10 - 1f
|
||||
0x16, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x1f, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef KYOSHO_A7105_INO
|
||||
const uint8_t PROGMEM KYOSHO_A7105_regs[] = {
|
||||
0xff, 0x42, 0xff, 0x25, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0x03, 0x1f, // 10 - 1f
|
||||
0x1e, 0x00, 0x00, 0xff, 0x00, 0x00, 0x23, 0x70, 0x1F, 0x47, 0x80, 0x57, 0x01, 0x45, 0x19, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
const uint8_t PROGMEM KYOSHO_HYPE_A7105_regs[] = {
|
||||
0xff, 0x42, 0x00, 0x10, 0xC0, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x05, 0x01, 0x04, // 00 - 0f
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x96, 0xc2, 0x1f, // 10 - 1f
|
||||
0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3a, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef WFLY2_A7105_INO //A7106 values
|
||||
const uint8_t PROGMEM WFLY2_A7105_regs[] = {
|
||||
0xff, 0x62, 0xff, 0x1F, 0x40, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x05, 0x00, 0x64, // 00 - 0f Changes: 0B:19->33, 0C:01,33
|
||||
0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0x03, 0x0f, // 10 - 1f 1C:4A->0A
|
||||
0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x23, 0x70, 0x15, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f 2B:77->03, 2E:19->18
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
#ifdef JOYSWAY_A7105_INO
|
||||
const uint8_t PROGMEM JOYSWAY_A7105_regs[] = {
|
||||
0xff, 0x62, 0xff, 0x0F, 0x00, 0xff, 0xff ,0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0xF5, 0x00, 0x15, // 00 - 0f
|
||||
0x9E, 0x4B, 0x00, 0x03, 0x56, 0x2B, 0x12, 0x4A, 0x02, 0x80, 0x80, 0x00, 0x0E, 0x91, 0x03, 0x0F, // 10 - 1f
|
||||
0x16, 0x2A, 0x00, 0xff, 0xff, 0xff, 0x3A, 0x06, 0x1F, 0x47, 0x80, 0x01, 0x05, 0x45, 0x18, 0x00, // 20 - 2f
|
||||
0x01, 0x0f // 30 - 31
|
||||
};
|
||||
#endif
|
||||
|
||||
#define ID_NORMAL 0x55201041
|
||||
#define ID_PLUS 0xAA201041
|
||||
void A7105_Init(void)
|
||||
{
|
||||
uint8_t *A7105_Regs=0;
|
||||
uint8_t vco_calibration0, vco_calibration1;
|
||||
|
||||
#ifdef JOYSWAY_A7105_INO
|
||||
if(protocol==PROTO_JOYSWAY)
|
||||
{
|
||||
A7105_Regs=(uint8_t*)JOYSWAY_A7105_regs;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef WFLY2_A7105_INO
|
||||
if(protocol==PROTO_WFLY2)
|
||||
{
|
||||
A7105_Regs=(uint8_t*)WFLY2_A7105_regs;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef HEIGHT_A7105_INO
|
||||
if(protocol==PROTO_HEIGHT)
|
||||
{
|
||||
A7105_Regs=(uint8_t*)HEIGHT_A7105_regs;
|
||||
A7105_WriteID(0x25A53C45);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef PELIKAN_A7105_INO
|
||||
if(protocol==PROTO_PELIKAN)
|
||||
{
|
||||
A7105_Regs=(uint8_t*)PELIKAN_A7105_regs;
|
||||
A7105_WriteID(0x06230623);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef BUGS_A7105_INO
|
||||
if(protocol==PROTO_BUGS)
|
||||
A7105_Regs=(uint8_t*)BUGS_A7105_regs;
|
||||
else
|
||||
#endif
|
||||
#ifdef HUBSAN_A7105_INO
|
||||
if(protocol==PROTO_HUBSAN)
|
||||
{
|
||||
A7105_WriteID(ID_NORMAL);
|
||||
A7105_Regs=(uint8_t*)HUBSAN_A7105_regs;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
A7105_WriteID(0x5475c52A);//0x2Ac57554
|
||||
#ifdef FLYSKY_A7105_INO
|
||||
if(protocol==PROTO_FLYSKY)
|
||||
A7105_Regs=(uint8_t*)FLYSKY_A7105_regs;
|
||||
#endif
|
||||
#if defined(AFHDS2A_A7105_INO) || defined(AFHDS2A_RX_A7105_INO)
|
||||
if(protocol==PROTO_AFHDS2A || protocol==PROTO_AFHDS2A_RX)
|
||||
A7105_Regs=(uint8_t*)AFHDS2A_A7105_regs;
|
||||
#endif
|
||||
#ifdef KYOSHO_A7105_INO
|
||||
if(protocol==PROTO_KYOSHO)
|
||||
{
|
||||
if(sub_protocol==KYOSHO_FHSS)
|
||||
A7105_Regs=(uint8_t*)KYOSHO_A7105_regs;
|
||||
else
|
||||
A7105_Regs=(uint8_t*)KYOSHO_HYPE_A7105_regs;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 0x32; i++)
|
||||
{
|
||||
uint8_t val=pgm_read_byte_near(&A7105_Regs[i]);
|
||||
#ifdef FLYSKY_A7105_INO
|
||||
if(protocol==PROTO_FLYSKY && sub_protocol==CX20)
|
||||
{
|
||||
if(i==0x0E) val=0x01;
|
||||
if(i==0x1F) val=0x1F;
|
||||
if(i==0x20) val=0x1E;
|
||||
}
|
||||
#endif
|
||||
#ifdef HEIGHT_A7105_INO
|
||||
if(protocol==PROTO_HEIGHT && sub_protocol==HEIGHT_8CH)
|
||||
if(i==0x03) val=0x0A;
|
||||
#endif
|
||||
if( val != 0xff)
|
||||
A7105_WriteReg(i, val);
|
||||
}
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
|
||||
if(protocol==PROTO_KYOSHO && sub_protocol==KYOSHO_FHSS)
|
||||
{//strange calibration...
|
||||
//IF Filter Bank Calibration
|
||||
A7105_WriteReg(A7105_02_CALC,0x0F);
|
||||
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
|
||||
// A7105_ReadReg(A7105_22_IF_CALIB_I);
|
||||
// A7105_ReadReg(A7105_24_VCO_CURCAL);
|
||||
// A7105_ReadReg(25_VCO_SBCAL_I);
|
||||
// A7105_ReadReg(1A_RX_GAIN_II);
|
||||
// A7105_ReadReg(1B_RX_GAIN_III);
|
||||
}
|
||||
else
|
||||
{
|
||||
//IF Filter Bank Calibration
|
||||
A7105_WriteReg(A7105_02_CALC,1);
|
||||
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
|
||||
// A7105_ReadReg(A7105_22_IF_CALIB_I);
|
||||
// A7105_ReadReg(A7105_24_VCO_CURCAL);
|
||||
|
||||
if(protocol==INIT_FLYSKY)
|
||||
if(protocol!=PROTO_HUBSAN)
|
||||
{
|
||||
//VCO Current Calibration
|
||||
A7105_WriteReg(A7105_24_VCO_CURCAL,0x13); //Recommended calibration from A7105 Datasheet
|
||||
@@ -234,20 +477,48 @@ void A7105_Init(uint8_t protocol)
|
||||
A7105_WriteReg(A7105_0F_CHANNEL, 0);
|
||||
A7105_WriteReg(A7105_02_CALC,2);
|
||||
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
|
||||
// A7105_ReadReg(A7105_25_VCO_SBCAL_I);
|
||||
vco_calibration0 = A7105_ReadReg(A7105_25_VCO_SBCAL_I);
|
||||
|
||||
//VCO Bank Calibrate channel A0
|
||||
A7105_WriteReg(A7105_0F_CHANNEL, 0xa0);
|
||||
A7105_WriteReg(A7105_02_CALC, 2);
|
||||
while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
|
||||
// A7105_ReadReg(A7105_25_VCO_SBCAL_I);
|
||||
|
||||
//Reset VCO Band calibration
|
||||
if(protocol==INIT_FLYSKY)
|
||||
A7105_WriteReg(A7105_25_VCO_SBCAL_I,0x08);
|
||||
vco_calibration1 = A7105_ReadReg(A7105_25_VCO_SBCAL_I);
|
||||
|
||||
if(protocol==PROTO_BUGS || protocol==PROTO_WFLY2)
|
||||
A7105_SetVCOBand(vco_calibration0 & 0x07, vco_calibration1 & 0x07); // Set calibration band value to best match
|
||||
else
|
||||
if(protocol!=PROTO_HUBSAN)
|
||||
{
|
||||
switch(protocol)
|
||||
{
|
||||
case PROTO_FLYSKY:
|
||||
vco_calibration1=0x08;
|
||||
break;
|
||||
case PROTO_HEIGHT:
|
||||
vco_calibration1=0x02;
|
||||
break;
|
||||
case PROTO_PELIKAN:
|
||||
case PROTO_KYOSHO: //sub_protocol Hype
|
||||
vco_calibration1=0x0C;
|
||||
break;
|
||||
case PROTO_JOYSWAY:
|
||||
vco_calibration1=0x09;
|
||||
break;
|
||||
default:
|
||||
vco_calibration1=0x0A;
|
||||
break;
|
||||
}
|
||||
A7105_WriteReg(A7105_25_VCO_SBCAL_I,vco_calibration1); //Reset VCO Band calibration
|
||||
}
|
||||
}
|
||||
A7105_SetTxRxMode(TX_EN);
|
||||
A7105_SetPower();
|
||||
|
||||
#ifdef USE_A7105_CH15_TUNING
|
||||
A7105_AdjustLOBaseFreq(0);
|
||||
#endif
|
||||
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
}
|
||||
#endif
|
||||
228
Multiprotocol/AFHDS2A_Rx_a7105.ino
Normal file
228
Multiprotocol/AFHDS2A_Rx_a7105.ino
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(AFHDS2A_RX_A7105_INO)
|
||||
|
||||
#include "iface_a7105.h"
|
||||
|
||||
#define AFHDS2A_RX_TXPACKET_SIZE 38
|
||||
#define AFHDS2A_RX_RXPACKET_SIZE 37
|
||||
#define AFHDS2A_RX_NUMFREQ 16
|
||||
|
||||
enum {
|
||||
AFHDS2A_RX_BIND1,
|
||||
AFHDS2A_RX_BIND2,
|
||||
AFHDS2A_RX_BIND3,
|
||||
AFHDS2A_RX_DATA
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) AFHDS2A_RX_build_telemetry_packet()
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
uint8_t idx = 0;
|
||||
|
||||
packet_in[idx++] = RX_LQI; // 0 - 130
|
||||
packet_in[idx++] = RX_RSSI;
|
||||
packet_in[idx++] = 0; // start channel
|
||||
packet_in[idx++] = 14; // number of channels in packet
|
||||
// pack channels
|
||||
for (uint8_t i = 0; i < 14; i++) {
|
||||
uint32_t val = packet[9+i*2] | (((packet[10+i*2])&0x0F) << 8);
|
||||
if (val < 860)
|
||||
val = 860;
|
||||
// convert ppm (860-2140) to Multi (0-2047)
|
||||
val = min(((val-860)<<3)/5, 2047);
|
||||
|
||||
bits |= val << bitsavailable;
|
||||
bitsavailable += 11;
|
||||
while (bitsavailable >= 8) {
|
||||
packet_in[idx++] = bits & 0xff;
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) AFHDS2A_RX_data_ready()
|
||||
{
|
||||
// check if FECF+CRCF Ok
|
||||
return !(A7105_ReadReg(A7105_00_MODE) & (1 << 5 | 1 << 6 | 1 << 0));
|
||||
}
|
||||
|
||||
void AFHDS2A_RX_init()
|
||||
{
|
||||
uint8_t i;
|
||||
A7105_Init();
|
||||
hopping_frequency_no = 0;
|
||||
packet_count = 0;
|
||||
rx_data_started = false;
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
A7105_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN);
|
||||
A7105_Strobe(A7105_RX);
|
||||
|
||||
if (IS_BIND_IN_PROGRESS) {
|
||||
phase = AFHDS2A_RX_BIND1;
|
||||
}
|
||||
else {
|
||||
uint16_t temp = AFHDS2A_RX_EEPROM_OFFSET;
|
||||
for (i = 0; i < 4; i++)
|
||||
rx_id[i] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
for (i = 0; i < AFHDS2A_RX_NUMFREQ; i++)
|
||||
hopping_frequency[i] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
phase = AFHDS2A_RX_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
#define AFHDS2A_RX_WAIT_WRITE 0x80
|
||||
|
||||
uint16_t AFHDS2A_RX_callback()
|
||||
{
|
||||
static int8_t read_retry;
|
||||
int16_t temp;
|
||||
uint8_t i;
|
||||
|
||||
#ifndef FORCE_AFHDS2A_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
if (rx_disable_lna != IS_POWER_FLAG_on) {
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
A7105_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN);
|
||||
}
|
||||
|
||||
switch(phase) {
|
||||
case AFHDS2A_RX_BIND1:
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
AFHDS2A_RX_init(); // Abort bind
|
||||
break;
|
||||
}
|
||||
debugln("bind p=%d", phase+1);
|
||||
if (AFHDS2A_RX_data_ready()) {
|
||||
A7105_ReadData(AFHDS2A_RX_TXPACKET_SIZE);
|
||||
if ((packet[0] == 0xbb && packet[9] == 0x01) || (packet[0] == 0xbc && packet[9] <= 0x02)) {
|
||||
memcpy(rx_id, &packet[1], 4); // TX id actually
|
||||
memcpy(hopping_frequency, &packet[11], AFHDS2A_RX_NUMFREQ);
|
||||
phase = AFHDS2A_RX_BIND2;
|
||||
debugln("phase bind2");
|
||||
}
|
||||
}
|
||||
A7105_WriteReg(A7105_0F_PLL_I, (packet_count++ & 1) ? 0x0D : 0x8C); // bind channels
|
||||
A7105_Strobe(A7105_RX);
|
||||
return 10000;
|
||||
|
||||
case AFHDS2A_RX_BIND2:
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
AFHDS2A_RX_init(); // Abort bind
|
||||
break;
|
||||
}
|
||||
// got 2nd bind packet from tx ?
|
||||
if (AFHDS2A_RX_data_ready()) {
|
||||
A7105_ReadData(AFHDS2A_RX_TXPACKET_SIZE);
|
||||
if ((packet[0] == 0xBC && packet[9] == 0x02 && packet[10] == 0x00) &&
|
||||
(memcmp(rx_id, &packet[1], 4) == 0) &&
|
||||
(memcmp(rx_tx_addr, &packet[5], 4) == 0)) {
|
||||
// save tx info to eeprom
|
||||
temp = AFHDS2A_RX_EEPROM_OFFSET;
|
||||
for (i = 0; i < 4; i++)
|
||||
eeprom_write_byte((EE_ADDR)temp++, rx_id[i]);
|
||||
for (i = 0; i < AFHDS2A_RX_NUMFREQ; i++)
|
||||
eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[i]);
|
||||
phase = AFHDS2A_RX_BIND3;
|
||||
debugln("phase bind3");
|
||||
packet_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
case AFHDS2A_RX_BIND3:
|
||||
debugln("bind p=%d", phase+1);
|
||||
// transmit response packet
|
||||
packet[0] = 0xBC;
|
||||
memcpy(&packet[1], rx_id, 4);
|
||||
memcpy(&packet[5], rx_tx_addr, 4);
|
||||
//packet[9] = 0x01;
|
||||
packet[10] = 0x00;
|
||||
memset(&packet[11], 0xFF, 26);
|
||||
A7105_SetTxRxMode(TX_EN);
|
||||
rx_disable_lna = !IS_POWER_FLAG_on;
|
||||
A7105_WriteData(AFHDS2A_RX_RXPACKET_SIZE, packet_count++ & 1 ? 0x0D : 0x8C);
|
||||
if(phase == AFHDS2A_RX_BIND3 && packet_count > 20)
|
||||
{
|
||||
debugln("done");
|
||||
BIND_DONE;
|
||||
AFHDS2A_RX_init(); // Restart protocol
|
||||
break;
|
||||
}
|
||||
phase |= AFHDS2A_RX_WAIT_WRITE;
|
||||
return 1700;
|
||||
|
||||
case AFHDS2A_RX_BIND2 | AFHDS2A_RX_WAIT_WRITE:
|
||||
//Wait for TX completion
|
||||
pps_timer = micros();
|
||||
while ((uint32_t)(micros() - pps_timer) < 700) // Wait max 700µs, using serial+telemetry exit in about 120µs
|
||||
if (!(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
A7105_Strobe(A7105_RX);
|
||||
case AFHDS2A_RX_BIND3 | AFHDS2A_RX_WAIT_WRITE:
|
||||
phase &= ~AFHDS2A_RX_WAIT_WRITE;
|
||||
return 10000;
|
||||
|
||||
case AFHDS2A_RX_DATA:
|
||||
if (AFHDS2A_RX_data_ready()) {
|
||||
A7105_ReadData(AFHDS2A_RX_TXPACKET_SIZE);
|
||||
if (memcmp(&packet[1], rx_id, 4) == 0 && memcmp(&packet[5], rx_tx_addr, 4) == 0) {
|
||||
if (packet[0] == 0x58 && packet[37] == 0x00 && (telemetry_link&0x7F) == 0) { // standard packet, send channels to TX
|
||||
int rssi = min(A7105_ReadReg(A7105_1D_RSSI_THOLD),160);
|
||||
RX_RSSI = map16b(rssi, 160, 8, 0, 128);
|
||||
AFHDS2A_RX_build_telemetry_packet();
|
||||
telemetry_link = 1;
|
||||
#ifdef SEND_CPPM
|
||||
if(sub_protocol>0)
|
||||
telemetry_link |= 0x80; // Disable telemetry output
|
||||
#endif
|
||||
}
|
||||
rx_data_started = true;
|
||||
read_retry = 10; // hop to next channel
|
||||
pps_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
// packets per second
|
||||
if (millis() - pps_timer >= 1000) {
|
||||
pps_timer = millis();
|
||||
debugln("%d pps", pps_counter);
|
||||
RX_LQI = pps_counter / 2;
|
||||
pps_counter = 0;
|
||||
}
|
||||
|
||||
// frequency hopping
|
||||
if (read_retry++ >= 10) {
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no >= AFHDS2A_RX_NUMFREQ)
|
||||
hopping_frequency_no = 0;
|
||||
A7105_WriteReg(A7105_0F_PLL_I, hopping_frequency[hopping_frequency_no]);
|
||||
A7105_Strobe(A7105_RX);
|
||||
if (rx_data_started)
|
||||
read_retry = 0;
|
||||
else
|
||||
read_retry = -127; // retry longer until first packet is catched
|
||||
}
|
||||
return 385;
|
||||
}
|
||||
return 3850; // never reached
|
||||
}
|
||||
|
||||
#endif
|
||||
449
Multiprotocol/AFHDS2A_a7105.ino
Normal file
449
Multiprotocol/AFHDS2A_a7105.ino
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/flysky_a7105.c dated 2015-09-28
|
||||
|
||||
#ifdef AFHDS2A_A7105_INO
|
||||
|
||||
#define AFHDS2A_TXPACKET_SIZE 38
|
||||
#define AFHDS2A_RXPACKET_SIZE 37
|
||||
#define AFHDS2A_NUMFREQ 16
|
||||
|
||||
#if not defined TELEMETRY
|
||||
uint8_t RX_LQI=0;
|
||||
#endif
|
||||
|
||||
enum{
|
||||
AFHDS2A_PACKET_STICKS,
|
||||
AFHDS2A_PACKET_SETTINGS,
|
||||
AFHDS2A_PACKET_FAILSAFE,
|
||||
};
|
||||
|
||||
enum{
|
||||
AFHDS2A_BIND1,
|
||||
AFHDS2A_BIND2,
|
||||
AFHDS2A_BIND3,
|
||||
AFHDS2A_BIND4,
|
||||
AFHDS2A_DATA_INIT,
|
||||
AFHDS2A_DATA,
|
||||
};
|
||||
|
||||
static void AFHDS2A_calc_channels()
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
uint32_t rnd = MProtocol_id;
|
||||
while (idx < AFHDS2A_NUMFREQ)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t band_no = ((((idx<<1) | ((idx>>1) & 0b01)) + rx_tx_addr[3]) & 0b11);
|
||||
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
|
||||
uint8_t next_ch = band_no*41 + 1 + ((rnd >> idx) % 41); // Channel range: 1..164
|
||||
|
||||
for (i = 0; i < idx; i++)
|
||||
{
|
||||
// Keep the distance 5 between the channels
|
||||
uint8_t distance;
|
||||
if (next_ch > hopping_frequency[i])
|
||||
distance = next_ch - hopping_frequency[i];
|
||||
else
|
||||
distance = hopping_frequency[i] - next_ch;
|
||||
|
||||
if (distance < 5) break;
|
||||
}
|
||||
|
||||
if (i != idx) continue;
|
||||
|
||||
hopping_frequency[idx++] = next_ch;
|
||||
}
|
||||
}
|
||||
|
||||
// telemetry sensors ID
|
||||
enum{
|
||||
AFHDS2A_SENSOR_RX_VOLTAGE = 0x00,
|
||||
AFHDS2A_SENSOR_RX_ERR_RATE = 0xfe,
|
||||
AFHDS2A_SENSOR_RX_RSSI = 0xfc,
|
||||
AFHDS2A_SENSOR_RX_NOISE = 0xfb,
|
||||
AFHDS2A_SENSOR_RX_SNR = 0xfa,
|
||||
AFHDS2A_SENSOR_A3_VOLTAGE = 0x03,
|
||||
};
|
||||
|
||||
#if defined(AFHDS2A_FW_TELEMETRY) || defined(AFHDS2A_HUB_TELEMETRY)
|
||||
static void AFHDS2A_update_telemetry()
|
||||
{
|
||||
// Read TX RSSI
|
||||
int16_t temp=256-(A7105_ReadReg(A7105_1D_RSSI_THOLD)*8)/5; // value from A7105 is between 8 for maximum signal strength to 160 or less
|
||||
if(temp<0) temp=0;
|
||||
else if(temp>255) temp=255;
|
||||
TX_RSSI=temp;
|
||||
// AA | TXID | rx_id | sensor id | sensor # | value 16 bit big endian | sensor id ......
|
||||
// AC | TXID | rx_id | sensor id | sensor # | length | bytes | sensor id ......
|
||||
#ifdef AFHDS2A_FW_TELEMETRY
|
||||
if (option & 0x80)
|
||||
{// forward 0xAA and 0xAC telemetry to TX, skip rx and tx id to save space
|
||||
packet_in[0]= TX_RSSI;
|
||||
debug("T(%02X)=",packet[0]);
|
||||
for(uint8_t i=9;i < AFHDS2A_RXPACKET_SIZE; i++)
|
||||
{
|
||||
packet_in[i-8]=packet[i];
|
||||
debug(" %02X",packet[i]);
|
||||
}
|
||||
packet_in[29]=packet[0]; // 0xAA Normal telemetry, 0xAC Extended telemetry
|
||||
telemetry_link=2;
|
||||
debugln("");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef AFHDS2A_HUB_TELEMETRY
|
||||
if(packet[0]==0xAA)
|
||||
{ // 0xAA Normal telemetry, 0xAC Extended telemetry not decoded here
|
||||
for(uint8_t sensor=0; sensor<7; sensor++)
|
||||
{
|
||||
// Send FrSkyD telemetry to TX
|
||||
uint8_t index = 9+(4*sensor);
|
||||
switch(packet[index])
|
||||
{
|
||||
case AFHDS2A_SENSOR_RX_VOLTAGE:
|
||||
//v_lipo1 = packet[index+3]<<8 | packet[index+2];
|
||||
v_lipo1 = packet[index+2];
|
||||
telemetry_link=1;
|
||||
break;
|
||||
case AFHDS2A_SENSOR_A3_VOLTAGE:
|
||||
v_lipo2 = (packet[index+3]<<5) | (packet[index+2]>>3); // allows to read voltage up to 4S
|
||||
telemetry_link=1;
|
||||
break;
|
||||
case AFHDS2A_SENSOR_RX_ERR_RATE:
|
||||
if(packet[index+2]<=100)
|
||||
RX_LQI=packet[index+2];
|
||||
break;
|
||||
case AFHDS2A_SENSOR_RX_RSSI:
|
||||
RX_RSSI = -packet[index+2];
|
||||
break;
|
||||
case 0xff: // end of data
|
||||
return;
|
||||
/*default:
|
||||
// unknown sensor ID
|
||||
break;*/
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void AFHDS2A_build_bind_packet()
|
||||
{
|
||||
uint8_t ch;
|
||||
memcpy( &packet[1], rx_tx_addr, 4);
|
||||
memset( &packet[5], 0xff, 4);
|
||||
packet[10]= 0x00;
|
||||
for(ch=0; ch<AFHDS2A_NUMFREQ; ch++)
|
||||
packet[11+ch] = hopping_frequency[ch];
|
||||
memset( &packet[27], 0xff, 10);
|
||||
packet[37] = 0x00;
|
||||
switch(phase)
|
||||
{
|
||||
case AFHDS2A_BIND1:
|
||||
packet[0] = 0xbb;
|
||||
packet[9] = 0x01;
|
||||
break;
|
||||
case AFHDS2A_BIND2:
|
||||
case AFHDS2A_BIND3:
|
||||
case AFHDS2A_BIND4:
|
||||
packet[0] = 0xbc;
|
||||
if(phase == AFHDS2A_BIND4)
|
||||
{
|
||||
memcpy( &packet[5], &rx_id, 4);
|
||||
memset( &packet[11], 0xff, 16);
|
||||
}
|
||||
packet[9] = phase-1;
|
||||
if(packet[9] > 0x02)
|
||||
packet[9] = 0x02;
|
||||
packet[27]= 0x01;
|
||||
packet[28]= 0x80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void AFHDS2A_build_packet(uint8_t type)
|
||||
{
|
||||
uint16_t val;
|
||||
memcpy( &packet[1], rx_tx_addr, 4);
|
||||
memcpy( &packet[5], rx_id, 4);
|
||||
switch(type)
|
||||
{
|
||||
case AFHDS2A_PACKET_STICKS:
|
||||
packet[0] = 0x58;
|
||||
//16 channels + RX_LQI on channel 17
|
||||
for(uint8_t ch=0; ch<num_ch; ch++)
|
||||
{
|
||||
if(ch == 16 // CH17=RX_LQI
|
||||
#ifdef AFHDS2A_LQI_CH
|
||||
|| ch == (AFHDS2A_LQI_CH-1) // override channel with LQI
|
||||
#endif
|
||||
)
|
||||
val = 2000 - 10*RX_LQI;
|
||||
else
|
||||
val = convert_channel_ppm(CH_AETR[ch]);
|
||||
if(ch<14)
|
||||
{
|
||||
packet[9 + ch*2] = val;
|
||||
packet[10 + ch*2] = (val>>8)&0x0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[10 + (ch-14)*6] |= (val)<<4;
|
||||
packet[12 + (ch-14)*6] |= (val)&0xF0;
|
||||
packet[14 + (ch-14)*6] |= (val>>4)&0xF0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AFHDS2A_PACKET_FAILSAFE:
|
||||
packet[0] = 0x56;
|
||||
for(uint8_t ch=0; ch<num_ch; ch++)
|
||||
{
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(ch<16)
|
||||
val = Failsafe_data[CH_AETR[ch]];
|
||||
else
|
||||
val = FAILSAFE_CHANNEL_NOPULSES;
|
||||
if(val!=FAILSAFE_CHANNEL_HOLD && val!=FAILSAFE_CHANNEL_NOPULSES)
|
||||
{ // Failsafe values
|
||||
val = (((val<<2)+val)>>3)+860;
|
||||
if(ch<14)
|
||||
{
|
||||
packet[9 + ch*2] = val;
|
||||
packet[10 + ch*2] = (val>>8)&0x0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[10 + (ch-14)*6] &= 0x0F;
|
||||
packet[10 + (ch-14)*6] |= (val)<<4;
|
||||
packet[12 + (ch-14)*6] &= 0x0F;
|
||||
packet[12 + (ch-14)*6] |= (val)&0xF0;
|
||||
packet[14 + (ch-14)*6] &= 0x0F;
|
||||
packet[14 + (ch-14)*6] |= (val>>4)&0xF0;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if(ch<14)
|
||||
{ // no values
|
||||
packet[9 + ch*2] = 0xff;
|
||||
packet[10+ ch*2] = 0xff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AFHDS2A_PACKET_SETTINGS:
|
||||
packet[0] = 0xaa;
|
||||
packet[9] = 0xfd;
|
||||
packet[10]= 0xff;
|
||||
val=5*(option & 0x7f)+50; // option value should be between 0 and 70 which gives a value between 50 and 400Hz
|
||||
if(val<50 || val>400) val=50; // default is 50Hz
|
||||
packet[11]= val;
|
||||
packet[12]= val >> 8;
|
||||
packet[13] = sub_protocol & 0x01; // 1 -> PPM output enabled
|
||||
packet[14]= 0x00;
|
||||
for(uint8_t i=15; i<37; i++)
|
||||
packet[i] = 0xff;
|
||||
packet[18] = 0x05; // ?
|
||||
packet[19] = 0xdc; // ?
|
||||
packet[20] = 0x05; // ?
|
||||
if(sub_protocol&2)
|
||||
packet[21] = 0xdd; // SBUS output enabled
|
||||
else
|
||||
packet[21] = 0xde; // IBUS
|
||||
break;
|
||||
}
|
||||
packet[37] = 0x00;
|
||||
}
|
||||
|
||||
#define AFHDS2A_WAIT_WRITE 0x80
|
||||
|
||||
#ifdef STM32_BOARD
|
||||
#define AFHDS2A_WRITE_TIME 1550
|
||||
#else
|
||||
#define AFHDS2A_WRITE_TIME 1700
|
||||
#endif
|
||||
|
||||
uint16_t AFHDS2A_callback()
|
||||
{
|
||||
static uint8_t packet_type;
|
||||
static uint16_t packet_counter;
|
||||
uint8_t data_rx=0;
|
||||
uint16_t start;
|
||||
#ifndef FORCE_AFHDS2A_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
switch(phase)
|
||||
{
|
||||
case AFHDS2A_BIND1:
|
||||
case AFHDS2A_BIND2:
|
||||
case AFHDS2A_BIND3:
|
||||
AFHDS2A_build_bind_packet();
|
||||
A7105_WriteData(AFHDS2A_TXPACKET_SIZE, packet_count%2 ? 0x0d : 0x8c);
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & (1<<5))) // removed FECF check due to issues with fs-x6b -> & (1<<5 | 1<<6)
|
||||
{ // CRCF Ok
|
||||
A7105_ReadData(AFHDS2A_RXPACKET_SIZE);
|
||||
if(packet[0] == 0xbc && packet[9] == 0x01)
|
||||
{
|
||||
uint16_t addr;
|
||||
if(RX_num<16)
|
||||
addr=AFHDS2A_EEPROM_OFFSET+RX_num*4;
|
||||
else
|
||||
addr=AFHDS2A_EEPROM_OFFSET2+(RX_num-16)*4;
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
{
|
||||
rx_id[i] = packet[5+i];
|
||||
eeprom_write_byte((EE_ADDR)(addr+i),rx_id[i]);
|
||||
}
|
||||
phase = AFHDS2A_BIND4;
|
||||
packet_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
packet_count++;
|
||||
if(IS_BIND_DONE)
|
||||
{ // exit bind if asked to do so from the GUI
|
||||
phase = AFHDS2A_BIND4;
|
||||
break;
|
||||
}
|
||||
phase |= AFHDS2A_WAIT_WRITE;
|
||||
return AFHDS2A_WRITE_TIME;
|
||||
case AFHDS2A_BIND1|AFHDS2A_WAIT_WRITE:
|
||||
case AFHDS2A_BIND2|AFHDS2A_WAIT_WRITE:
|
||||
case AFHDS2A_BIND3|AFHDS2A_WAIT_WRITE:
|
||||
//Wait for TX completion
|
||||
start=micros();
|
||||
while ((uint16_t)((uint16_t)micros()-start) < 700) // Wait max 700µs, using serial+telemetry exit in about 120µs
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
A7105_SetPower();
|
||||
A7105_SetTxRxMode(TXRX_OFF); // Turn LNA off since we are in near range and we want to prevent swamping
|
||||
A7105_Strobe(A7105_RX);
|
||||
phase &= ~AFHDS2A_WAIT_WRITE;
|
||||
phase++;
|
||||
if(phase > AFHDS2A_BIND3)
|
||||
phase = AFHDS2A_BIND1;
|
||||
return 3850-AFHDS2A_WRITE_TIME;
|
||||
case AFHDS2A_BIND4:
|
||||
AFHDS2A_build_bind_packet();
|
||||
A7105_WriteData(AFHDS2A_TXPACKET_SIZE, packet_count%2 ? 0x0d : 0x8c);
|
||||
packet_count++;
|
||||
bind_phase++;
|
||||
if(bind_phase>=4)
|
||||
{
|
||||
hopping_frequency_no=1;
|
||||
phase = AFHDS2A_DATA_INIT;
|
||||
BIND_DONE;
|
||||
}
|
||||
break;
|
||||
case AFHDS2A_DATA_INIT:
|
||||
packet_counter=0;
|
||||
packet_type = AFHDS2A_PACKET_STICKS;
|
||||
phase = AFHDS2A_DATA;
|
||||
case AFHDS2A_DATA:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(3850);
|
||||
#endif
|
||||
AFHDS2A_build_packet(packet_type);
|
||||
data_rx=A7105_ReadReg(A7105_00_MODE); // Check if something has been received...
|
||||
A7105_WriteData(AFHDS2A_TXPACKET_SIZE, hopping_frequency[hopping_frequency_no++]);
|
||||
if(hopping_frequency_no >= AFHDS2A_NUMFREQ)
|
||||
hopping_frequency_no = 0;
|
||||
if(!(packet_counter % 1313))
|
||||
packet_type = AFHDS2A_PACKET_SETTINGS;
|
||||
else
|
||||
{
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(!(packet_counter % 1569) && IS_FAILSAFE_VALUES_on)
|
||||
{
|
||||
packet_type = AFHDS2A_PACKET_FAILSAFE;
|
||||
FAILSAFE_VALUES_off;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
packet_type = AFHDS2A_PACKET_STICKS; // todo : check for settings changes
|
||||
}
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & (1<<5)) && !(data_rx & 1)) // removed FECF check due to issues with fs-x6b -> & (1<<5 | 1<<6)
|
||||
{ // RX+CRCF Ok
|
||||
A7105_ReadData(AFHDS2A_RXPACKET_SIZE);
|
||||
if(packet[0] == 0xAA && packet[9] == 0xFC)
|
||||
packet_type=AFHDS2A_PACKET_SETTINGS; // RX is asking for settings
|
||||
else
|
||||
if((packet[0] == 0xAA && packet[9]!=0xFD) || packet[0] == 0xAC)
|
||||
{// Normal telemetry packet, ignore packets which contain the RX configuration: AA FD FF 32 00 01 00 FF FF FF 05 DC 05 DE FA FF FF FF FF FF FF FF FF FF FF FF FF FF FF
|
||||
if(!memcmp(&packet[1], rx_tx_addr, 4))
|
||||
{ // TX address validated
|
||||
for(uint8_t sensor=0; sensor<7; sensor++)
|
||||
{//read LQI value for RX output
|
||||
uint8_t index = 9+(4*sensor);
|
||||
if(packet[index]==AFHDS2A_SENSOR_RX_ERR_RATE && packet[index+2]<=100)
|
||||
{
|
||||
RX_LQI=packet[index+2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(AFHDS2A_FW_TELEMETRY) || defined(AFHDS2A_HUB_TELEMETRY)
|
||||
AFHDS2A_update_telemetry();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
packet_counter++;
|
||||
phase |= AFHDS2A_WAIT_WRITE;
|
||||
return AFHDS2A_WRITE_TIME;
|
||||
case AFHDS2A_DATA|AFHDS2A_WAIT_WRITE:
|
||||
//Wait for TX completion
|
||||
start=micros();
|
||||
while ((uint16_t)((uint16_t)micros()-start) < 700) // Wait max 700µs, using serial+telemetry exit in about 120µs
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
A7105_SetPower();
|
||||
A7105_SetTxRxMode(RX_EN);
|
||||
A7105_Strobe(A7105_RX);
|
||||
phase &= ~AFHDS2A_WAIT_WRITE;
|
||||
return 3850-AFHDS2A_WRITE_TIME;
|
||||
}
|
||||
return 3850;
|
||||
}
|
||||
|
||||
void AFHDS2A_init()
|
||||
{
|
||||
A7105_Init();
|
||||
|
||||
AFHDS2A_calc_channels();
|
||||
packet_count = 0;
|
||||
bind_phase = 0;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
phase = AFHDS2A_BIND1;
|
||||
else
|
||||
{
|
||||
phase = AFHDS2A_DATA_INIT;
|
||||
//Read RX ID from EEPROM based on RX_num, RX_num must be uniq for each RX
|
||||
uint16_t addr;
|
||||
if(RX_num<16)
|
||||
addr=AFHDS2A_EEPROM_OFFSET+RX_num*4;
|
||||
else
|
||||
addr=AFHDS2A_EEPROM_OFFSET2+(RX_num-16)*4;
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
rx_id[i]=eeprom_read_byte((EE_ADDR)(addr+i));
|
||||
}
|
||||
hopping_frequency_no = 0;
|
||||
if(sub_protocol&0x04)
|
||||
num_ch=17;
|
||||
else
|
||||
num_ch=14;
|
||||
}
|
||||
#endif
|
||||
179
Multiprotocol/ASSAN_nrf24l01.ino
Normal file
179
Multiprotocol/ASSAN_nrf24l01.ino
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(ASSAN_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define ASSAN_PACKET_SIZE 20
|
||||
#define ASSAN_RF_BIND_CHANNEL 0x03
|
||||
#define ASSAN_ADDRESS_LENGTH 4
|
||||
|
||||
enum {
|
||||
ASSAN_BIND0=0,
|
||||
ASSAN_BIND1,
|
||||
ASSAN_BIND2,
|
||||
ASSAN_DATA0,
|
||||
ASSAN_DATA1,
|
||||
ASSAN_DATA2,
|
||||
ASSAN_DATA3,
|
||||
ASSAN_DATA4,
|
||||
ASSAN_DATA5
|
||||
};
|
||||
|
||||
void ASSAN_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
//Specifics to ASSAN
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x02); // 4 bytes rx/tx address
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x80\x80\x80\xB8", ASSAN_ADDRESS_LENGTH); // Bind address
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x80\x80\x80\xB8", ASSAN_ADDRESS_LENGTH); // Bind address
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, ASSAN_PACKET_SIZE);
|
||||
}
|
||||
|
||||
void ASSAN_send_packet()
|
||||
{
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
{
|
||||
uint16_t val=Channel_data[i];
|
||||
val=((val<<2)+val)+(860<<3); // PPM value <<3
|
||||
|
||||
packet[2*i]=val>>8;
|
||||
packet[2*i+1]=val;
|
||||
}
|
||||
for(uint8_t i=0;i<ASSAN_ADDRESS_LENGTH;i++)
|
||||
packet[16+i]=packet[23-i];
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, ASSAN_PACKET_SIZE);
|
||||
}
|
||||
|
||||
uint16_t ASSAN_callback()
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
// Bind
|
||||
case ASSAN_BIND0:
|
||||
//Config RX @1M
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, ASSAN_RF_BIND_CHANNEL);
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
phase++;
|
||||
case ASSAN_BIND1:
|
||||
//Wait for receiver to send the frames
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ //Something has been received
|
||||
NRF24L01_ReadPayload(packet, ASSAN_PACKET_SIZE);
|
||||
if(packet[19]==0x13)
|
||||
{ //Last frame received
|
||||
phase++;
|
||||
//Switch to TX
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
//Prepare bind packet
|
||||
memset(packet,0x05,ASSAN_PACKET_SIZE-5);
|
||||
packet[15]=0x99;
|
||||
for(uint8_t i=0;i<ASSAN_ADDRESS_LENGTH;i++)
|
||||
packet[16+i]=packet[23-i];
|
||||
packet_count=0;
|
||||
delayMilliseconds(260);
|
||||
return 10000; // Wait 270ms in total...
|
||||
}
|
||||
}
|
||||
return 1000;
|
||||
case ASSAN_BIND2:
|
||||
// Send 20 packets
|
||||
packet_count++;
|
||||
if(packet_count==20)
|
||||
packet[15]=0x13; // different value for last packet
|
||||
NRF24L01_WritePayload(packet, ASSAN_PACKET_SIZE);
|
||||
if(packet_count==20)
|
||||
{
|
||||
phase++;
|
||||
delayMilliseconds(2165);
|
||||
}
|
||||
return 22520;
|
||||
// Normal operation
|
||||
case ASSAN_DATA0:
|
||||
// Bind Done
|
||||
BIND_DONE;
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
case ASSAN_DATA1:
|
||||
case ASSAN_DATA4:
|
||||
// Change ID and RF channel
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR,packet+20+4*hopping_frequency_no, ASSAN_ADDRESS_LENGTH);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
hopping_frequency_no^=0x01;
|
||||
NRF24L01_SetPower();
|
||||
phase=ASSAN_DATA2;
|
||||
return 2000;
|
||||
case ASSAN_DATA2:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(12000);
|
||||
#endif
|
||||
case ASSAN_DATA3:
|
||||
ASSAN_send_packet();
|
||||
phase++; // DATA 3 or 4
|
||||
return 5000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ASSAN_initialize_txid()
|
||||
{
|
||||
/* //Renaud TXID with Freq=36 and alternate Freq 67 or 68 or 69 or 70 or 71 or 73 or 74 or 75 or 78 and may be more...
|
||||
packet[23]=0x22;
|
||||
packet[22]=0x37;
|
||||
packet[21]=0xFA;
|
||||
packet[20]=0x53; */
|
||||
// Using packet[20..23] to store the ID1 and packet[24..27] to store the ID2
|
||||
uint8_t freq=0,freq2;
|
||||
for(uint8_t i=0;i<ASSAN_ADDRESS_LENGTH;i++)
|
||||
{
|
||||
uint8_t temp=rx_tx_addr[i];
|
||||
packet[i+20]=temp;
|
||||
packet[i+24]=temp+1;
|
||||
freq+=temp;
|
||||
}
|
||||
|
||||
// Main frequency
|
||||
freq=((freq%25)+2)<<1;
|
||||
if(freq&0x02) freq|=0x01;
|
||||
hopping_frequency[0]=freq;
|
||||
// Alternate frequency has some random
|
||||
do
|
||||
{
|
||||
freq2=random(0xfefefefe)%9;
|
||||
freq2+=freq*2-5;
|
||||
}
|
||||
while( (freq2>118) || (freq2<freq+1) || (freq2==2*freq) );
|
||||
hopping_frequency[1]=freq2;
|
||||
}
|
||||
|
||||
void ASSAN_init()
|
||||
{
|
||||
ASSAN_initialize_txid();
|
||||
ASSAN_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
phase=ASSAN_BIND0;
|
||||
else
|
||||
phase=ASSAN_DATA0;
|
||||
}
|
||||
|
||||
#endif
|
||||
159
Multiprotocol/Arduino.ino
Normal file
159
Multiprotocol/Arduino.ino
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/************************************/
|
||||
/************************************/
|
||||
/** Arduino replacement routines **/
|
||||
/************************************/
|
||||
// replacement map()
|
||||
int16_t map16b( int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
|
||||
{
|
||||
// return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
long y ;
|
||||
x -= in_min ;
|
||||
y = out_max - out_min ;
|
||||
y *= x ;
|
||||
x = y / (in_max - in_min) ;
|
||||
return x + out_min ;
|
||||
}
|
||||
|
||||
#ifndef STM32_BOARD
|
||||
int16_t map( int16_t x, int16_t in_min, int16_t in_max, int16_t out_min, int16_t out_max)
|
||||
{
|
||||
// return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
long y ;
|
||||
x -= in_min ;
|
||||
y = out_max - out_min ;
|
||||
y *= x ;
|
||||
x = y / (in_max - in_min) ;
|
||||
return x + out_min ;
|
||||
}
|
||||
|
||||
// replacement millis() and micros()
|
||||
// These work polled, no interrupts
|
||||
// micros() MUST be called at least once every 32 milliseconds
|
||||
uint16_t MillisPrecount ;
|
||||
uint16_t lastTimerValue ;
|
||||
uint32_t TotalMicros ;
|
||||
uint32_t TotalMillis ;
|
||||
uint8_t Correction ;
|
||||
|
||||
uint32_t micros()
|
||||
{
|
||||
uint16_t elapsed ;
|
||||
uint8_t millisToAdd ;
|
||||
uint8_t oldSREG = SREG ;
|
||||
cli() ;
|
||||
uint16_t time = TCNT1 ; // Read timer 1
|
||||
SREG = oldSREG ;
|
||||
|
||||
elapsed = time - lastTimerValue ;
|
||||
elapsed += Correction ;
|
||||
Correction = elapsed & 0x01 ;
|
||||
elapsed >>= 1 ;
|
||||
|
||||
uint32_t ltime = TotalMicros ;
|
||||
ltime += elapsed ;
|
||||
cli() ;
|
||||
TotalMicros = ltime ; // Done this way for RPM to work correctly
|
||||
lastTimerValue = time ;
|
||||
SREG = oldSREG ; // Still valid from above
|
||||
|
||||
elapsed += MillisPrecount;
|
||||
millisToAdd = 0 ;
|
||||
|
||||
if ( elapsed > 15999 )
|
||||
{
|
||||
millisToAdd = 16 ;
|
||||
elapsed -= 16000 ;
|
||||
}
|
||||
if ( elapsed > 7999 )
|
||||
{
|
||||
millisToAdd += 8 ;
|
||||
elapsed -= 8000 ;
|
||||
}
|
||||
if ( elapsed > 3999 )
|
||||
{
|
||||
millisToAdd += 4 ;
|
||||
elapsed -= 4000 ;
|
||||
}
|
||||
if ( elapsed > 1999 )
|
||||
{
|
||||
millisToAdd += 2 ;
|
||||
elapsed -= 2000 ;
|
||||
}
|
||||
if ( elapsed > 999 )
|
||||
{
|
||||
millisToAdd += 1 ;
|
||||
elapsed -= 1000 ;
|
||||
}
|
||||
TotalMillis += millisToAdd ;
|
||||
MillisPrecount = elapsed ;
|
||||
return TotalMicros ;
|
||||
}
|
||||
|
||||
uint32_t millis()
|
||||
{
|
||||
micros() ;
|
||||
return TotalMillis ;
|
||||
}
|
||||
|
||||
void delayMilliseconds(unsigned long ms)
|
||||
{
|
||||
uint16_t start = (uint16_t)micros();
|
||||
uint16_t lms = ms ;
|
||||
|
||||
while (lms > 0) {
|
||||
if ((uint16_t)((uint16_t)micros() - start) >= 1000) {
|
||||
lms--;
|
||||
start += 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Important notes:
|
||||
- Max value is 16000µs
|
||||
- delay is not accurate due to interrupts happening */
|
||||
void delayMicroseconds(unsigned int us)
|
||||
{
|
||||
if (--us == 0)
|
||||
return;
|
||||
us <<= 2; // * 4
|
||||
us -= 2; // - 2
|
||||
#ifdef ORANGE_TX
|
||||
__asm__ __volatile__ (
|
||||
"1: sbiw %0,1" "\n\t" // 2 cycles
|
||||
"nop \n"
|
||||
"nop \n"
|
||||
"nop \n"
|
||||
"nop \n"
|
||||
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
|
||||
);
|
||||
#else
|
||||
__asm__ __volatile__ (
|
||||
"1: sbiw %0,1" "\n\t" // 2 cycles
|
||||
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ORANGE_TX
|
||||
void init()
|
||||
{
|
||||
// this needs to be called before setup() or some functions won't work there
|
||||
sei();
|
||||
}
|
||||
#endif //ORANGE_TX
|
||||
|
||||
#endif //STM32_BOARD
|
||||
372
Multiprotocol/BUGSMINI_nrf24l01.ino
Normal file
372
Multiprotocol/BUGSMINI_nrf24l01.ino
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with MJX Bugs 3 Mini and Bugs 3H
|
||||
|
||||
#if defined(BUGSMINI_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define BUGSMINI_INITIAL_WAIT 500
|
||||
#define BUGSMINI_PACKET_INTERVAL 6840
|
||||
#define BUGSMINI_WRITE_WAIT 2000
|
||||
#define BUGSMINI_TX_PAYLOAD_SIZE 24
|
||||
#define BUGSMINI_RX_PAYLOAD_SIZE 16
|
||||
#define BUGSMINI_NUM_RF_CHANNELS 15
|
||||
#define BUGSMINI_ADDRESS_SIZE 5
|
||||
|
||||
static uint8_t BUGSMINI_txid[3];
|
||||
static uint8_t BUGSMINI_txhash;
|
||||
|
||||
enum {
|
||||
BUGSMINI_BIND1,
|
||||
BUGSMINI_BIND2,
|
||||
BUGSMINI_DATA1,
|
||||
BUGSMINI_DATA2
|
||||
};
|
||||
|
||||
#define BUGSMINI_CH_SW_ARM CH5_SW
|
||||
#define BUGSMINI_CH_SW_ANGLE CH6_SW
|
||||
#define BUGSMINI_CH_SW_FLIP CH7_SW
|
||||
#define BUGSMINI_CH_SW_PICTURE CH8_SW
|
||||
#define BUGSMINI_CH_SW_VIDEO CH9_SW
|
||||
#define BUGSMINI_CH_SW_LED CH10_SW
|
||||
#define BUGSMINI_CH_SW_ALTHOLD CH11_SW
|
||||
|
||||
// flags packet[12]
|
||||
#define BUGSMINI_FLAG_FLIP 0x08 // automatic flip
|
||||
#define BUGSMINI_FLAG_MODE 0x04 // low/high speed select (set is high speed)
|
||||
#define BUGSMINI_FLAG_VIDEO 0x02 // toggle video
|
||||
#define BUGSMINI_FLAG_PICTURE 0x01 // toggle picture
|
||||
|
||||
// flags packet[13]
|
||||
#define BUGSMINI_FLAG_LED 0x80 // enable LEDs
|
||||
#define BUGSMINI_FLAG_ARM 0x40 // arm (toggle to turn on motors)
|
||||
#define BUGSMINI_FLAG_DISARM 0x20 // disarm (toggle to turn off motors)
|
||||
#define BUGSMINI_FLAG_ANGLE 0x02 // angle/acro mode (set is angle mode)
|
||||
#define BUGSMINI_FLAG_ALTHOLD 0x04 // angle/altitude hold mode (set is altitude mode)
|
||||
|
||||
static void __attribute__((unused)) BUGSMINI_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BUGSMINI_RX_PAYLOAD_SIZE); // bytes of data payload for rx pipe 1
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) BUGSMINI_check_arming()
|
||||
{
|
||||
uint8_t arm_channel = BUGSMINI_CH_SW_ARM;
|
||||
|
||||
if (arm_channel != arm_channel_previous)
|
||||
{
|
||||
arm_channel_previous = arm_channel;
|
||||
if (arm_channel)
|
||||
{
|
||||
armed = 1;
|
||||
arm_flags ^= BUGSMINI_FLAG_ARM;
|
||||
}
|
||||
else
|
||||
{
|
||||
armed = 0;
|
||||
arm_flags ^= BUGSMINI_FLAG_DISARM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) BUGSMINI_send_packet()
|
||||
{
|
||||
BUGSMINI_check_arming(); // sets globals arm_flags and armed
|
||||
|
||||
uint16_t aileron = convert_channel_16b_limit(AILERON,500,0);
|
||||
uint16_t elevator = convert_channel_16b_limit(ELEVATOR,0,500);
|
||||
uint16_t throttle = armed ? convert_channel_16b_limit(THROTTLE,0,500) : 0;
|
||||
uint16_t rudder = convert_channel_16b_limit(RUDDER,500,0);
|
||||
|
||||
packet[1] = BUGSMINI_txid[0];
|
||||
packet[2] = BUGSMINI_txid[1];
|
||||
packet[3] = BUGSMINI_txid[2];
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[4] = 0x00;
|
||||
packet[5] = 0x7d;
|
||||
packet[6] = 0x7d;
|
||||
packet[7] = 0x7d;
|
||||
packet[8] = 0x20;
|
||||
packet[9] = 0x20;
|
||||
packet[10]= 0x20;
|
||||
packet[11]= 0x40;
|
||||
packet[12]^= 0x40; // alternating freq hopping flag
|
||||
packet[13]= 0x60;
|
||||
packet[14]= 0x00;
|
||||
packet[15]= 0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[4] = throttle >> 1;
|
||||
packet[5] = rudder >> 1;
|
||||
packet[6] = elevator >> 1;
|
||||
packet[7] = aileron >> 1;
|
||||
packet[8] = (((aileron / 5) >> 1) + 7) // dynamic trim 0x07 - 0x39
|
||||
| (aileron << 7);
|
||||
packet[9] = (((elevator / 5) >> 1) + 7) // dynamic trim 0x07 - 0x39
|
||||
| (elevator << 7);
|
||||
packet[10]= (((rudder / 5) >> 1) + 7) // dynamic trim 0x07 - 0x39
|
||||
| (rudder << 7);
|
||||
packet[11]= 0x40 | (throttle << 7);
|
||||
packet[12]= 0x80 | ((packet[12] ^ 0x40) & 0x40)
|
||||
| BUGSMINI_FLAG_MODE
|
||||
| GET_FLAG(BUGSMINI_CH_SW_PICTURE, BUGSMINI_FLAG_PICTURE)
|
||||
| GET_FLAG(BUGSMINI_CH_SW_VIDEO, BUGSMINI_FLAG_VIDEO);
|
||||
if(armed)
|
||||
packet[12] |= GET_FLAG(BUGSMINI_CH_SW_FLIP, BUGSMINI_FLAG_FLIP);
|
||||
packet[13] = arm_flags
|
||||
| GET_FLAG(BUGSMINI_CH_SW_LED, BUGSMINI_FLAG_LED)
|
||||
| GET_FLAG(BUGSMINI_CH_SW_ALTHOLD, BUGSMINI_FLAG_ALTHOLD)
|
||||
| GET_FLAG(BUGSMINI_CH_SW_ANGLE, BUGSMINI_FLAG_ANGLE);
|
||||
// BUGS3H althold -> BUGSMINI_FLAG_ALTHOLD|BUGSMINI_FLAG_ANGLE , angle -> 0
|
||||
packet[14] = 0;
|
||||
packet[15] = 0; // a lot of 0x53 and some 0x52 on bugs 3H
|
||||
}
|
||||
uint8_t checksum = 0x6d;
|
||||
for(uint8_t i=1; i < BUGSMINI_TX_PAYLOAD_SIZE; i++)
|
||||
checksum ^= packet[i];
|
||||
packet[0] = checksum;
|
||||
|
||||
if(!(packet[12]&0x40))
|
||||
{
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no >= BUGSMINI_NUM_RF_CHANNELS)
|
||||
hopping_frequency_no = 0;
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? hopping_frequency[hopping_frequency_no+BUGSMINI_NUM_RF_CHANNELS] : hopping_frequency[hopping_frequency_no]);
|
||||
}
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, BUGSMINI_TX_PAYLOAD_SIZE);
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
// compute final address for the rxid received during bind
|
||||
// thanks to Pascal for the function!
|
||||
const uint8_t PROGMEM BUGSMINI_end []= {
|
||||
0x2d,0x9e ,0x95,0xa4 ,0x9c,0x5c ,0xb4,0xa6 ,0xa9,0xce ,0x56,0x2b ,0x3e,0x73 ,0xb8,0x95 ,0x6a,0x82,
|
||||
0x94,0x37 ,0x3d,0x5a ,0x4b,0xb2 ,0x69,0x49 ,0xc2,0x24 ,0x6b,0x3d ,0x23,0xc6 ,0x9e,0xa3 ,0xa4,0x98,
|
||||
0x5c,0x9e ,0xa6,0x52 ,0xce,0x76 ,0x2b,0x4b ,0x73,0x3a };
|
||||
static void __attribute__((unused)) BUGSMINI_make_address()
|
||||
{
|
||||
uint8_t start, length, index;
|
||||
|
||||
//read rxid
|
||||
uint8_t base_adr=BUGSMINI_EEPROM_OFFSET+(RX_num&0x0F)*2;
|
||||
uint8_t rxid_high = eeprom_read_byte((EE_ADDR)(base_adr+0));
|
||||
uint8_t rxid_low = eeprom_read_byte((EE_ADDR)(base_adr+1));
|
||||
|
||||
if(rxid_high==0x00 || rxid_high==0xFF)
|
||||
rx_tx_addr[0]=0x52;
|
||||
else
|
||||
rx_tx_addr[0]=rxid_high;
|
||||
|
||||
rx_tx_addr[1]=BUGSMINI_txhash;
|
||||
|
||||
if(rxid_low==0x00 || rxid_low==0xFF)
|
||||
rx_tx_addr[2]=0x66;
|
||||
else
|
||||
rx_tx_addr[2]=rxid_low;
|
||||
|
||||
for(uint8_t end_idx=0;end_idx<23;end_idx++)
|
||||
{
|
||||
//calculate sequence start
|
||||
if(end_idx<=7)
|
||||
start=end_idx;
|
||||
else
|
||||
start=(end_idx-7)*16+7;
|
||||
//calculate sequence length
|
||||
if(end_idx>6)
|
||||
{
|
||||
if(end_idx>15)
|
||||
length=(23-end_idx)<<1;
|
||||
else
|
||||
length=16;
|
||||
}
|
||||
else
|
||||
length=(end_idx+1)<<1;
|
||||
//calculate first index
|
||||
index=start-rxid_high;
|
||||
//scan for a possible match using the current end
|
||||
for(uint8_t i=0;i<length;i++)
|
||||
{
|
||||
if(index==rxid_low)
|
||||
{ //match found
|
||||
rx_tx_addr[3]=pgm_read_byte_near( &BUGSMINI_end[end_idx<<1] );
|
||||
rx_tx_addr[4]=pgm_read_byte_near( &BUGSMINI_end[(end_idx<<1)+1] );
|
||||
return;
|
||||
}
|
||||
index+=i&1?7:8; //increment index
|
||||
}
|
||||
}
|
||||
// Something wrong happened if we arrive here....
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) BUGSMINI_update_telemetry()
|
||||
{
|
||||
#if defined(BUGS_HUB_TELEMETRY)
|
||||
uint8_t checksum = 0x6d;
|
||||
for(uint8_t i=1; i<12; i++)
|
||||
checksum += packet[i];
|
||||
if(packet[0] == checksum)
|
||||
{
|
||||
RX_RSSI = packet[3];
|
||||
if(sub_protocol==BUGS3H)
|
||||
{
|
||||
if(packet[11] & 0x40)
|
||||
v_lipo1 = 0x40; // Warning
|
||||
else if(packet[11] & 0x80)
|
||||
v_lipo1 = 0x20; // Critical
|
||||
else
|
||||
v_lipo1 = 0x80; // Ok
|
||||
}
|
||||
else
|
||||
{
|
||||
if(packet[11] & 0x80)
|
||||
v_lipo1 = 0x80; // Ok
|
||||
else if(packet[11] & 0x40)
|
||||
v_lipo1 = 0x40; // Warning
|
||||
else
|
||||
v_lipo1 = 0x20; // Critical
|
||||
}
|
||||
telemetry_link=1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t BUGSMINI_callback()
|
||||
{
|
||||
uint8_t base_adr;
|
||||
switch(phase)
|
||||
{
|
||||
case BUGSMINI_BIND1:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
XN297_ReadPayload(packet, BUGSMINI_RX_PAYLOAD_SIZE);
|
||||
base_adr=BUGSMINI_EEPROM_OFFSET+(RX_num&0x0F)*2;
|
||||
eeprom_write_byte((EE_ADDR)(base_adr+0),packet[1]); // Save rxid in EEPROM
|
||||
eeprom_write_byte((EE_ADDR)(base_adr+1),packet[2]); // Save rxid in EEPROM
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
BUGSMINI_make_address();
|
||||
XN297_SetTXAddr(rx_tx_addr, 5);
|
||||
XN297_SetRXAddr(rx_tx_addr, 5);
|
||||
phase = BUGSMINI_DATA1;
|
||||
BIND_DONE;
|
||||
return BUGSMINI_PACKET_INTERVAL;
|
||||
}
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
BUGSMINI_send_packet();
|
||||
phase = BUGSMINI_BIND2;
|
||||
return BUGSMINI_WRITE_WAIT;
|
||||
case BUGSMINI_BIND2:
|
||||
// switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_FlushRx();
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
|
||||
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
|
||||
phase = BUGSMINI_BIND1;
|
||||
return BUGSMINI_PACKET_INTERVAL - BUGSMINI_WRITE_WAIT;
|
||||
case BUGSMINI_DATA1:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(BUGSMINI_PACKET_INTERVAL);
|
||||
#endif
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready => read only 12 bytes to not overwrite channel change flag
|
||||
XN297_ReadPayload(packet, 12);
|
||||
BUGSMINI_update_telemetry();
|
||||
}
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
BUGSMINI_send_packet();
|
||||
phase = BUGSMINI_DATA2;
|
||||
return BUGSMINI_WRITE_WAIT;
|
||||
case BUGSMINI_DATA2:
|
||||
// switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
|
||||
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
|
||||
phase = BUGSMINI_DATA1;
|
||||
return BUGSMINI_PACKET_INTERVAL - BUGSMINI_WRITE_WAIT;
|
||||
}
|
||||
return BUGSMINI_PACKET_INTERVAL;
|
||||
}
|
||||
|
||||
#define BUGSMINI_NUM_TX_RF_MAPS 4
|
||||
// haven't figured out BUGSMINI_txid<-->rf channel mapping yet
|
||||
const uint8_t PROGMEM BUGSMINI_RF_chans[BUGSMINI_NUM_TX_RF_MAPS][BUGSMINI_NUM_RF_CHANNELS] = {
|
||||
{0x22,0x2f,0x3a,0x14,0x20,0x2d,0x38,0x18,0x26,0x32,0x11,0x1d,0x29,0x35,0x17},
|
||||
{0x3d,0x34,0x2b,0x22,0x19,0x40,0x37,0x2e,0x25,0x1c,0x3a,0x31,0x28,0x1f,0x16},
|
||||
{0x12,0x20,0x2f,0x1a,0x28,0x38,0x14,0x23,0x32,0x1c,0x2c,0x3b,0x17,0x26,0x34},
|
||||
{0x13,0x25,0x37,0x1F,0x31,0x17,0x28,0x3A,0x1C,0x2E,0x22,0x33,0x19,0x2B,0x3D} };
|
||||
const uint8_t PROGMEM BUGSMINI_bind_chans[BUGSMINI_NUM_RF_CHANNELS] = {
|
||||
0x1A,0x23,0x2C,0x35,0x3E,0x17,0x20,0x29,0x32,0x3B,0x14,0x1D,0x26,0x2F,0x38}; // bugs 3 mini bind channels
|
||||
const uint8_t PROGMEM BUGSMINI_tx_id[BUGSMINI_NUM_TX_RF_MAPS][3] = {
|
||||
{0xA8,0xE6,0x32},
|
||||
{0xdd,0xab,0xfd},
|
||||
{0x90,0x9e,0x4a},
|
||||
{0x20,0x28,0xBA} };
|
||||
const uint8_t PROGMEM BUGSMINI_tx_hash[BUGSMINI_NUM_TX_RF_MAPS] = { // 2nd byte of final address
|
||||
0x6c,0x9e,0x3d,0xb3};
|
||||
|
||||
static void __attribute__((unused)) BUGSMINI_initialize_txid()
|
||||
{
|
||||
// load hopping_frequency with tx channels in low part and bind channels in high part
|
||||
for(uint8_t i=0; i<BUGSMINI_NUM_RF_CHANNELS;i++)
|
||||
{
|
||||
hopping_frequency[i]=pgm_read_byte_near( &BUGSMINI_RF_chans[rx_tx_addr[3]%BUGSMINI_NUM_TX_RF_MAPS][i] );
|
||||
hopping_frequency[i+BUGSMINI_NUM_RF_CHANNELS]=pgm_read_byte_near( &BUGSMINI_bind_chans[i] );
|
||||
}
|
||||
// load txid
|
||||
for(uint8_t i=0; i<sizeof(BUGSMINI_txid);i++)
|
||||
BUGSMINI_txid[i]=pgm_read_byte_near( &BUGSMINI_tx_id[rx_tx_addr[3]%BUGSMINI_NUM_TX_RF_MAPS][i] );
|
||||
//load tx_hash
|
||||
BUGSMINI_txhash = pgm_read_byte_near( &BUGSMINI_tx_hash[rx_tx_addr[3]%BUGSMINI_NUM_TX_RF_MAPS] );
|
||||
}
|
||||
|
||||
void BUGSMINI_init()
|
||||
{
|
||||
BUGSMINI_initialize_txid();
|
||||
memset(packet, (uint8_t)0, BUGSMINI_TX_PAYLOAD_SIZE);
|
||||
BUGSMINI_RF_init();
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
XN297_SetTXAddr((const uint8_t*)"mjxRC", 5);
|
||||
XN297_SetRXAddr((const uint8_t*)"mjxRC", 5);
|
||||
phase = BUGSMINI_BIND1;
|
||||
}
|
||||
else
|
||||
{
|
||||
BUGSMINI_make_address();
|
||||
XN297_SetTXAddr(rx_tx_addr, 5);
|
||||
XN297_SetRXAddr(rx_tx_addr, 5);
|
||||
phase = BUGSMINI_DATA1;
|
||||
}
|
||||
armed = 0;
|
||||
arm_flags = BUGSMINI_FLAG_DISARM; // initial value from captures
|
||||
arm_channel_previous = BUGSMINI_CH_SW_ARM;
|
||||
}
|
||||
|
||||
#endif
|
||||
203
Multiprotocol/Bayang_Rx_nrf24l01.ino
Normal file
203
Multiprotocol/Bayang_Rx_nrf24l01.ino
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(BAYANG_RX_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define BAYANG_RX_PACKET_SIZE 15
|
||||
#define BAYANG_RX_RF_NUM_CHANNELS 4
|
||||
#define BAYANG_RX_RF_BIND_CHANNEL 0
|
||||
#define BAYANG_RX_ADDRESS_LENGTH 5
|
||||
|
||||
enum {
|
||||
BAYANG_RX_BIND = 0,
|
||||
BAYANG_RX_DATA
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) Bayang_Rx_init_nrf24l01()
|
||||
{
|
||||
const uint8_t bind_address[BAYANG_RX_ADDRESS_LENGTH] = { 0,0,0,0,0 };
|
||||
NRF24L01_Initialize();
|
||||
XN297_SetTXAddr(bind_address, BAYANG_RX_ADDRESS_LENGTH);
|
||||
XN297_SetRXAddr(bind_address, BAYANG_RX_ADDRESS_LENGTH);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BAYANG_RX_PACKET_SIZE + 2); // 2 extra bytes for xn297 crc
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, BAYANG_RX_RF_BIND_CHANNEL);
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) Bayang_Rx_check_validity() {
|
||||
uint8_t sum = packet[0];
|
||||
for (uint8_t i = 1; i < BAYANG_RX_PACKET_SIZE - 1; i++)
|
||||
sum += packet[i];
|
||||
return sum == packet[14];
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) Bayang_Rx_build_telemetry_packet()
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
uint8_t idx = 0;
|
||||
|
||||
packet_in[idx++] = RX_LQI;
|
||||
packet_in[idx++] = RX_LQI>>1; // no RSSI: 125..0
|
||||
packet_in[idx++] = 0; // start channel
|
||||
packet_in[idx++] = 10; // number of channels in packet
|
||||
|
||||
// convert & pack channels
|
||||
for (uint8_t i = 0; i < packet_in[3]; i++) {
|
||||
uint32_t val = CHANNEL_MIN_100;
|
||||
if (i < 4) {
|
||||
// AETR
|
||||
//val = (((packet[4 + i * 2] & ~0x7C) << 8) | packet[5 + i * 2]) << 1;
|
||||
val=packet[4 + i * 2]&0x03;
|
||||
val=(val<<8)+packet[5 + i * 2];
|
||||
val=((val+128)<<3)/5;
|
||||
} else if (i == 4 || i == 5) {
|
||||
val=packet[i==4?1:13];
|
||||
val=((val+32)<<5)/5; // extra analog channel
|
||||
} else if (((i == 6) && (packet[2] & 0x08)) || // flip
|
||||
((i == 7) && (packet[2] & 0x01)) || // rth
|
||||
((i == 8) && (packet[2] & 0x20)) || // picture
|
||||
((i == 9) && (packet[2] & 0x10))) { // video
|
||||
// set channel to 100% if feature is enabled
|
||||
val = CHANNEL_MAX_100;
|
||||
}
|
||||
bits |= val << bitsavailable;
|
||||
bitsavailable += 11;
|
||||
while (bitsavailable >= 8) {
|
||||
packet_in[idx++] = bits & 0xff;
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BAYANG_RX_init()
|
||||
{
|
||||
uint8_t i;
|
||||
Bayang_Rx_init_nrf24l01();
|
||||
hopping_frequency_no = 0;
|
||||
rx_data_started = false;
|
||||
rx_data_received = false;
|
||||
|
||||
if (IS_BIND_IN_PROGRESS) {
|
||||
phase = BAYANG_RX_BIND;
|
||||
}
|
||||
else {
|
||||
uint16_t temp = BAYANG_RX_EEPROM_OFFSET;
|
||||
for (i = 0; i < 5; i++)
|
||||
rx_tx_addr[i] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
for (i = 0; i < BAYANG_RX_RF_NUM_CHANNELS; i++)
|
||||
hopping_frequency[i] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
XN297_SetTXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
|
||||
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
|
||||
phase = BAYANG_RX_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t BAYANG_RX_callback()
|
||||
{
|
||||
uint8_t i;
|
||||
static int8_t read_retry;
|
||||
|
||||
switch (phase) {
|
||||
case BAYANG_RX_BIND:
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
BAYANG_RX_init(); // Abort bind
|
||||
break;
|
||||
}
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) {
|
||||
// data received from TX
|
||||
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && ( packet[0] == 0xA4 || packet[0] == 0xA2 ) && Bayang_Rx_check_validity()) {
|
||||
// store tx info into eeprom
|
||||
uint16_t temp = BAYANG_RX_EEPROM_OFFSET;
|
||||
for (i = 0; i < 5; i++) {
|
||||
rx_tx_addr[i] = packet[i + 1];
|
||||
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[i]);
|
||||
}
|
||||
for (i = 0; i < 4; i++) {
|
||||
hopping_frequency[i] = packet[i + 6];
|
||||
eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[i]);
|
||||
}
|
||||
XN297_SetTXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
|
||||
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
|
||||
BIND_DONE;
|
||||
phase = BAYANG_RX_DATA;
|
||||
}
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
}
|
||||
break;
|
||||
case BAYANG_RX_DATA:
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) {
|
||||
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && packet[0] == 0xA5 && Bayang_Rx_check_validity()) {
|
||||
if ((telemetry_link & 0x7F) == 0) {
|
||||
Bayang_Rx_build_telemetry_packet();
|
||||
telemetry_link = 1;
|
||||
#ifdef SEND_CPPM
|
||||
if(sub_protocol>0)
|
||||
telemetry_link |= 0x80; // Disable telemetry output
|
||||
#endif
|
||||
}
|
||||
rx_data_started = true;
|
||||
rx_data_received = true;
|
||||
read_retry = 8;
|
||||
pps_counter++;
|
||||
}
|
||||
}
|
||||
// packets per second
|
||||
if (millis() - pps_timer >= 1000) {
|
||||
pps_timer = millis();
|
||||
debugln("%d pps", pps_counter);
|
||||
RX_LQI = pps_counter >> 1;
|
||||
pps_counter = 0;
|
||||
}
|
||||
// frequency hopping
|
||||
if (read_retry++ >= 8) {
|
||||
hopping_frequency_no++;
|
||||
if (hopping_frequency_no >= BAYANG_RX_RF_NUM_CHANNELS)
|
||||
hopping_frequency_no = 0;
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
if (rx_data_started)
|
||||
{
|
||||
if(rx_data_received)
|
||||
{ // In sync
|
||||
rx_data_received = false;
|
||||
read_retry = 5;
|
||||
return 1500;
|
||||
}
|
||||
else
|
||||
{ // packet lost
|
||||
read_retry = 0;
|
||||
if(RX_LQI==0) // communication lost
|
||||
rx_data_started=false;
|
||||
}
|
||||
}
|
||||
else
|
||||
read_retry = -16; // retry longer until first packet is caught
|
||||
}
|
||||
return 250;
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,7 +12,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with EAchine H8 mini, H10, BayangToys X6/X7/X9, JJRC JJ850 ...
|
||||
// Compatible with EAchine H8 mini, H10, BayangToys X6/X7/X9, JJRC JJ850 ...
|
||||
// Last sync with hexfet new_protocols/bayang_nrf24l01.c dated 2015-12-22
|
||||
|
||||
#if defined(BAYANG_NRF24L01_INO)
|
||||
|
||||
@@ -20,158 +21,342 @@
|
||||
|
||||
#define BAYANG_BIND_COUNT 1000
|
||||
#define BAYANG_PACKET_PERIOD 2000
|
||||
#define BAYANG_PACKET_TELEM_PERIOD 5000
|
||||
#define BAYANG_INITIAL_WAIT 500
|
||||
#define BAYANG_PACKET_SIZE 15
|
||||
#define BAYANG_RF_NUM_CHANNELS 4
|
||||
#define BAYANG_RF_BIND_CHANNEL 0
|
||||
#define BAYANG_RF_BIND_CHANNEL_X16_AH 10
|
||||
#define BAYANG_ADDRESS_LENGTH 5
|
||||
|
||||
enum BAYANG_FLAGS {
|
||||
// flags going to packet[2]
|
||||
BAYANG_FLAG_RTH = 0x01,
|
||||
BAYANG_FLAG_HEADLESS = 0x02,
|
||||
BAYANG_FLAG_FLIP = 0x08
|
||||
BAYANG_FLAG_FLIP = 0x08,
|
||||
BAYANG_FLAG_VIDEO = 0x10,
|
||||
BAYANG_FLAG_PICTURE = 0x20,
|
||||
// flags going to packet[3]
|
||||
BAYANG_FLAG_INVERTED = 0x80, // inverted flight on Floureon H101
|
||||
BAYANG_FLAG_TAKE_OFF = 0x20, // take off / landing on X16 AH
|
||||
BAYANG_FLAG_EMG_STOP = 0x04|0x08, // 0x08 for VISUO XS809H-W-HD-G
|
||||
};
|
||||
|
||||
enum BAYANG_PHASES {
|
||||
BAYANG_BIND = 0,
|
||||
BAYANG_DATA
|
||||
enum BAYANG_OPTION_FLAGS {
|
||||
BAYANG_OPTION_FLAG_TELEMETRY = 0x01,
|
||||
BAYANG_OPTION_FLAG_ANALOGAUX = 0x02,
|
||||
};
|
||||
|
||||
void BAYANG_send_packet(uint8_t bind)
|
||||
static void __attribute__((unused)) BAYANG_send_packet()
|
||||
{
|
||||
uint8_t i;
|
||||
if (bind)
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
if(option & BAYANG_OPTION_FLAG_TELEMETRY)
|
||||
if(option & BAYANG_OPTION_FLAG_ANALOGAUX)
|
||||
packet[0]= 0xA1; // telemetry and analog aux are enabled
|
||||
else
|
||||
packet[0]= 0xA3; // telemetry is enabled
|
||||
else if(option & BAYANG_OPTION_FLAG_ANALOGAUX)
|
||||
packet[0]= 0xA2; // analog aux is enabled
|
||||
else
|
||||
#else
|
||||
if(option & BAYANG_OPTION_FLAG_ANALOGAUX)
|
||||
packet[0]= 0xA2; // analog aux is enabled
|
||||
else
|
||||
#endif
|
||||
packet[0]= 0xA4;
|
||||
if(sub_protocol==QX100)
|
||||
packet[0] = 0x53;
|
||||
|
||||
for(i=0;i<5;i++)
|
||||
packet[i+1]=rx_tx_addr[i];
|
||||
for(i=0;i<4;i++)
|
||||
packet[i+6]=hopping_frequency[i];
|
||||
packet[10] = rx_tx_addr[0];
|
||||
packet[11] = rx_tx_addr[1];
|
||||
switch (sub_protocol)
|
||||
{
|
||||
case QX100:
|
||||
case X16_AH:
|
||||
packet[10] = 0x00;
|
||||
packet[11] = 0x00;
|
||||
break;
|
||||
case IRDRONE:
|
||||
packet[10] = 0x30;
|
||||
packet[11] = 0x01;
|
||||
break;
|
||||
case DHD_D4:
|
||||
packet[10] = 0xC8;
|
||||
packet[11] = 0x99;
|
||||
break;
|
||||
default:
|
||||
packet[10] = rx_tx_addr[0]; // txid[0]
|
||||
packet[11] = rx_tx_addr[1]; // txid[1]
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t val;
|
||||
uint8_t dyntrim = 1;
|
||||
switch (sub_protocol)
|
||||
{
|
||||
case X16_AH:
|
||||
case IRDRONE:
|
||||
packet[0] = 0xA6;
|
||||
break;
|
||||
default:
|
||||
packet[0] = 0xA5;
|
||||
packet[1] = 0xFA; // normal mode is 0xf7, expert 0xfa
|
||||
break;
|
||||
}
|
||||
if (option & BAYANG_OPTION_FLAG_ANALOGAUX)
|
||||
{
|
||||
// Analog aux channel 1 (channel 14)
|
||||
packet[1] = convert_channel_8b(CH14);
|
||||
}
|
||||
else
|
||||
packet[1] = 0xFA; // normal mode is 0xF7, expert 0xFa , D4 normal is 0xF4
|
||||
|
||||
//Flags
|
||||
//Flags packet[2]
|
||||
packet[2] = 0x00;
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[2] |= BAYANG_FLAG_FLIP;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
packet[2] |= BAYANG_FLAG_HEADLESS;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
if(CH5_SW)
|
||||
packet[2] = BAYANG_FLAG_FLIP;
|
||||
if(CH6_SW)
|
||||
packet[2] |= BAYANG_FLAG_RTH;
|
||||
|
||||
if(CH7_SW)
|
||||
packet[2] |= BAYANG_FLAG_PICTURE;
|
||||
if(CH8_SW)
|
||||
packet[2] |= BAYANG_FLAG_VIDEO;
|
||||
if(CH9_SW)
|
||||
{
|
||||
packet[2] |= BAYANG_FLAG_HEADLESS;
|
||||
dyntrim = 0;
|
||||
}
|
||||
//Flags packet[3]
|
||||
packet[3] = 0x00;
|
||||
|
||||
if(CH10_SW)
|
||||
packet[3] = BAYANG_FLAG_INVERTED;
|
||||
if(CH11_SW)
|
||||
dyntrim = 0;
|
||||
if(CH12_SW)
|
||||
packet[3] |= BAYANG_FLAG_TAKE_OFF;
|
||||
if(CH13_SW)
|
||||
packet[3] |= BAYANG_FLAG_EMG_STOP;
|
||||
//Aileron
|
||||
val = convert_channel_10b(AILERON);
|
||||
packet[4] = (val>>8) + ((val>>2) & 0xFC);
|
||||
val = convert_channel_10b(AILERON, false);
|
||||
packet[4] = (val>>8) + (dyntrim ? ((val>>2) & 0xFC) : 0x7C);
|
||||
packet[5] = val & 0xFF;
|
||||
//Elevator
|
||||
val = convert_channel_10b(ELEVATOR);
|
||||
packet[6] = (val>>8) + ((val>>2) & 0xFC);
|
||||
val = convert_channel_10b(ELEVATOR, false);
|
||||
packet[6] = (val>>8) + (dyntrim ? ((val>>2) & 0xFC) : 0x7C);
|
||||
packet[7] = val & 0xFF;
|
||||
//Throttle
|
||||
val = convert_channel_10b(THROTTLE);
|
||||
val = convert_channel_10b(THROTTLE, false);
|
||||
packet[8] = (val>>8) + 0x7C;
|
||||
packet[9] = val & 0xFF;
|
||||
//Rudder
|
||||
val = convert_channel_10b(RUDDER);
|
||||
packet[10] = (val>>8) + (val>>2 & 0xFC);
|
||||
val = convert_channel_10b(RUDDER, false);
|
||||
packet[10] = (val>>8) + (dyntrim ? ((val>>2) & 0xFC) : 0x7C);
|
||||
packet[11] = val & 0xFF;
|
||||
}
|
||||
packet[12] = rx_tx_addr[2];
|
||||
switch (sub_protocol)
|
||||
{
|
||||
case H8S3D:
|
||||
packet[12] = rx_tx_addr[2]; // txid[2]
|
||||
packet[13] = 0x34;
|
||||
break;
|
||||
case QX100:
|
||||
case X16_AH:
|
||||
packet[12] = 0;
|
||||
packet[13] = 0;
|
||||
break;
|
||||
case IRDRONE:
|
||||
packet[12] = 0xE0;
|
||||
packet[13] = 0x2E;
|
||||
break;
|
||||
case DHD_D4:
|
||||
packet[12] = 0x37; //0x17 during bind
|
||||
packet[13] = 0xED;
|
||||
break;
|
||||
default:
|
||||
packet[12] = rx_tx_addr[2]; // txid[2]
|
||||
if (option & BAYANG_OPTION_FLAG_ANALOGAUX)
|
||||
{ // Analog aux channel 2 (channel 15)
|
||||
packet[13] = convert_channel_8b(CH15);
|
||||
}
|
||||
else
|
||||
packet[13] = 0x0A;
|
||||
break;
|
||||
}
|
||||
packet[14] = 0;
|
||||
for (uint8_t i=0; i < BAYANG_PACKET_SIZE-1; i++)
|
||||
packet[14] += packet[i];
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
|
||||
|
||||
if (bind)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, BAYANG_RF_BIND_CHANNEL);
|
||||
else
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? rf_ch_num:hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no%=BAYANG_RF_NUM_CHANNELS;
|
||||
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
XN297_WritePayload(packet, BAYANG_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
void BAYANG_init()
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
static void __attribute__((unused)) BAYANG_check_rx(void)
|
||||
{
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // data received from model
|
||||
XN297_ReadPayload(packet, BAYANG_PACKET_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 255);
|
||||
|
||||
NRF24L01_FlushRx();
|
||||
uint8_t check = packet[0];
|
||||
for (uint8_t i=1; i < BAYANG_PACKET_SIZE-1; i++)
|
||||
check += packet[i];
|
||||
// decode data , check sum is ok as well, since there is no crc
|
||||
if (packet[0] == 0x85 && packet[14] == check && telemetry_link == 0)
|
||||
{
|
||||
// uncompensated battery volts*100/2
|
||||
v_lipo1 = (packet[3]<<7) + (packet[4]>>1);
|
||||
// compensated battery volts*100/2
|
||||
v_lipo2 = (packet[5]<<7) + (packet[6]>>1);
|
||||
// reception in packets / sec
|
||||
RX_LQI = packet[7];
|
||||
RX_RSSI = RX_LQI;
|
||||
//Flags
|
||||
//uint8_t flags = packet[3] >> 3;
|
||||
// battery low: flags & 1
|
||||
telemetry_link=1;
|
||||
#if defined HUB_TELEMETRY
|
||||
// Multiplexed P, I, D values in packet[8] and packet[9].
|
||||
// The two most significant bits specify which term is sent.
|
||||
// Remaining 14 bits represent the value: 0 .. 16383
|
||||
frsky_send_user_frame(0x24+(packet[8]>>6), packet[9], packet[8] & 0x3F ); //0x24 = ACCEL_X_ID, so ACCEL_X_ID=P, ACCEL_Y_ID=I, ACCEL_Z_ID=D
|
||||
#endif
|
||||
telemetry_counter++;
|
||||
if(telemetry_lost)
|
||||
telemetry_link=0; // Don't send anything yet
|
||||
}
|
||||
}
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) BAYANG_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
|
||||
XN297_SetTXAddr((uint8_t *)"\x00\x00\x00\x00\x00", BAYANG_ADDRESS_LENGTH);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BAYANG_PACKET_SIZE);
|
||||
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03);
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_SetPower();
|
||||
|
||||
NRF24L01_Activate(0x73); // Activate feature register
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x01);
|
||||
NRF24L01_Activate(0x73);
|
||||
switch (sub_protocol)
|
||||
{
|
||||
case X16_AH:
|
||||
case IRDRONE:
|
||||
rf_ch_num = BAYANG_RF_BIND_CHANNEL_X16_AH;
|
||||
break;
|
||||
default:
|
||||
rf_ch_num = BAYANG_RF_BIND_CHANNEL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
BAYANG_BIND=0,
|
||||
BAYANG_WRITE,
|
||||
BAYANG_CHECK,
|
||||
BAYANG_READ,
|
||||
};
|
||||
|
||||
#define BAYANG_CHECK_DELAY 1000 // Time after write phase to check write complete
|
||||
#define BAYANG_READ_DELAY 600 // Time before read phase
|
||||
|
||||
uint16_t BAYANG_callback()
|
||||
{
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
uint16_t start;
|
||||
#endif
|
||||
switch(phase)
|
||||
{
|
||||
case BAYANG_BIND:
|
||||
if (bind_counter == 0)
|
||||
if (--bind_counter == 0)
|
||||
{
|
||||
XN297_SetTXAddr(rx_tx_addr, BAYANG_ADDRESS_LENGTH);
|
||||
phase = BAYANG_DATA;
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
XN297_SetRXAddr(rx_tx_addr, BAYANG_ADDRESS_LENGTH);
|
||||
#endif
|
||||
BIND_DONE;
|
||||
phase++; //WRITE
|
||||
}
|
||||
else
|
||||
BAYANG_send_packet();
|
||||
break;
|
||||
case BAYANG_WRITE:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync((option & BAYANG_OPTION_FLAG_TELEMETRY)?BAYANG_PACKET_TELEM_PERIOD:BAYANG_PACKET_PERIOD);
|
||||
#endif
|
||||
BAYANG_send_packet();
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
if (option & BAYANG_OPTION_FLAG_TELEMETRY)
|
||||
{ // telemetry is enabled
|
||||
state++;
|
||||
if (state > 200)
|
||||
{
|
||||
BAYANG_send_packet(1);
|
||||
bind_counter--;
|
||||
state = 0;
|
||||
//telemetry reception packet rate - packets per second
|
||||
TX_LQI = telemetry_counter>>1;
|
||||
telemetry_counter = 0;
|
||||
telemetry_lost=0;
|
||||
}
|
||||
phase++; //CHECK
|
||||
return BAYANG_CHECK_DELAY;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case BAYANG_DATA:
|
||||
BAYANG_send_packet(0);
|
||||
#ifdef BAYANG_HUB_TELEMETRY
|
||||
case BAYANG_CHECK:
|
||||
// switch radio to rx as soon as packet is sent
|
||||
start=(uint16_t)micros();
|
||||
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 1000) // Wait max 1ms
|
||||
if((NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS)))
|
||||
break;
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x03);
|
||||
phase++; // READ
|
||||
return BAYANG_PACKET_TELEM_PERIOD - BAYANG_CHECK_DELAY - BAYANG_READ_DELAY;
|
||||
case BAYANG_READ:
|
||||
BAYANG_check_rx();
|
||||
phase=BAYANG_WRITE;
|
||||
return BAYANG_READ_DELAY;
|
||||
#endif
|
||||
}
|
||||
return BAYANG_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void BAYANG_initialize_txid()
|
||||
static void __attribute__((unused)) BAYANG_initialize_txid()
|
||||
{
|
||||
// Strange txid, rx_tx_addr and rf_channels could be anything so I will use on rx_tx_addr for all of them...
|
||||
// Strange also that there is no check of duplicated rf channels... I think we need to implement that later...
|
||||
for(uint8_t i=0; i<BAYANG_RF_NUM_CHANNELS; i++)
|
||||
hopping_frequency[i]=rx_tx_addr[i]%42;
|
||||
//Could be using txid[0..2] but using rx_tx_addr everywhere instead...
|
||||
if(sub_protocol==DHD_D4)
|
||||
hopping_frequency[0]=(rx_tx_addr[2]&0x07)|0x01;
|
||||
else
|
||||
hopping_frequency[0]=0;
|
||||
hopping_frequency[1]=(rx_tx_addr[3]&0x1F)+0x10;
|
||||
hopping_frequency[2]=hopping_frequency[1]+0x20;
|
||||
hopping_frequency[3]=hopping_frequency[2]+0x20;
|
||||
hopping_frequency_no=0;
|
||||
}
|
||||
|
||||
uint16_t initBAYANG(void)
|
||||
void BAYANG_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
phase=BAYANG_BIND;
|
||||
bind_counter = BAYANG_BIND_COUNT;
|
||||
BAYANG_initialize_txid();
|
||||
phase=BAYANG_BIND;
|
||||
BAYANG_init();
|
||||
return BAYANG_INITIAL_WAIT+BAYANG_PACKET_PERIOD;
|
||||
BAYANG_RF_init();
|
||||
packet_count=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
202
Multiprotocol/Binary_Signature.ino
Normal file
202
Multiprotocol/Binary_Signature.ino
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/************************/
|
||||
/** Firmware Signature **/
|
||||
/************************/
|
||||
|
||||
/*
|
||||
The firmware signature is appended to the compiled binary image in order to provide information
|
||||
about the options used to compile the firmware file. This information is then used by Multi-module
|
||||
flashing tools to verify that the image is correct / valid.
|
||||
|
||||
In order for the build process to determine the options used to build the firmware this file conditionally
|
||||
declares 'flag' variables for the options we are interested in.
|
||||
|
||||
When the pre-compiler parses the source code these variables are either present or not in the parsed cpp file,
|
||||
typically '$build_dir$/preproc/ctags_target_for_gcc_minus_e.cpp'.
|
||||
|
||||
Once the .bin file is created an additional command-line build tool scans the parsed cpp file, detects the
|
||||
flags, assembles the signature, and finally appends the signature to the end of the binary file.
|
||||
|
||||
The signature is 24 bytes long:
|
||||
multi-x[8-byte hex code]-[8-byte version number]
|
||||
|
||||
For example:
|
||||
multi-x1234abcd-01020199
|
||||
|
||||
The 8-byte hex code is a 32-bit bitmask value indicating the configuration options, currently:
|
||||
|
||||
Bit(s) Bitmask Option Comment
|
||||
1-2 0x3 Module type Read as a two-bit value indicating a number from 0-3 which maps to a module type (AVR, STM32, OrangeRX)
|
||||
3-7 0x7C Channel order Read as a five-bit value indicating a number from 0-23 which maps to as channel order (AETR, TAER, RETA, etc) (right-shift two bits to read)
|
||||
8 0x80 Bootloader support Indicates whether or not the firmware was built with support for the bootloader
|
||||
9 0x100 CHECK_FOR_BOOTLOADER Indicates if CHECK_FOR_BOOTLOADER is defined
|
||||
10 0x200 INVERT_TELEMETRY Indicates if INVERT_TELEMETRY is defined
|
||||
11 0x400 MULTI_STATUS Indicates if MULTI_STATUS is defined
|
||||
12 0x800 MULTI_TELEMETRY Indicates if MULTI_TELEMETRY is defined
|
||||
13 0x1000 DEBUG_SERIAL Indicates if DEBUG_SERIAL is defined
|
||||
14-16 0xE000 Module sub-type Reads as a three-bit value indicating a number from 0-7 which maps to a module sub-type (right-shift 13 bits to read)
|
||||
|
||||
The 8-byte version number is the version number zero-padded to a fixed width of two-bytes per segment and no separator.
|
||||
E.g. 1.2.3.45 becomes 01020345.
|
||||
|
||||
Multi Telemetery Type can be read from bits 11 and 12 using the bitmask 0xC00 and right-shifting ten bits:
|
||||
Telemetry Type Decimal Value Binary Value
|
||||
Undefined 0 00
|
||||
erSkyTX 1 01
|
||||
OpenTX 2 10
|
||||
|
||||
Module types are mapped to the following decimal / binary values:
|
||||
Module Type Decimal Value Binary Valsue
|
||||
AVR (Atmega328p) 0 00
|
||||
STM32 (F103) 1 01
|
||||
OrangeRX (Xmega) 2 10
|
||||
|
||||
Module sub-type is currently used for STM32F103 only and is mapped as follows:
|
||||
Module Type Sub Type Decimal Value Binary Value
|
||||
STM32 (F103) STM32F103CB 0 000
|
||||
STM32 (F103) STM32F103C8 1 001
|
||||
STM32 (F103) T18 5in1 2 010
|
||||
|
||||
Channel orders are mapped to the following decimal / binary values:
|
||||
Channel Order Decimal Value Binary Value
|
||||
AETR 0 00000
|
||||
AERT 1 00001
|
||||
ARET 2 00010
|
||||
ARTE 3 00011
|
||||
ATRE 4 00100
|
||||
ATER 5 00101
|
||||
EATR 6 00110
|
||||
EART 7 00111
|
||||
ERAT 8 01000
|
||||
ERTA 9 01001
|
||||
ETRA 10 01010
|
||||
ETAR 11 01011
|
||||
TEAR 12 01100
|
||||
TERA 13 01101
|
||||
TREA 14 01110
|
||||
TRAE 15 01111
|
||||
TARE 16 10000
|
||||
TAER 17 10001
|
||||
RETA 18 10010
|
||||
REAT 19 10011
|
||||
RAET 20 10100
|
||||
RATE 21 10101
|
||||
RTAE 22 10110
|
||||
RTEA 23 10111
|
||||
*/
|
||||
|
||||
// Set the flags for detecting and writing the firmware signature
|
||||
#if defined (CHECK_FOR_BOOTLOADER)
|
||||
bool firmwareFlag_CHECK_FOR_BOOTLOADER = true;
|
||||
#endif
|
||||
#if defined (INVERT_TELEMETRY)
|
||||
bool firmwareFlag_INVERT_TELEMETRY = true;
|
||||
#endif
|
||||
#if defined (MULTI_STATUS)
|
||||
bool firmwareFlag_MULTI_STATUS = true;
|
||||
#endif
|
||||
#if defined (MULTI_TELEMETRY)
|
||||
bool firmwareFlag_MULTI_TELEMETRY = true;
|
||||
#endif
|
||||
#if defined (DEBUG_SERIAL)
|
||||
bool firmwareFlag_DEBUG_SERIAL = true;
|
||||
#endif
|
||||
|
||||
// STM32 Module sub-type flags
|
||||
#if defined (MCU_STM32F103CB)
|
||||
bool firmwareFlag_MCU_STM32F103CB = true;
|
||||
#endif
|
||||
#if defined (MCU_STM32F103C8)
|
||||
bool firmwareFlag_MCU_STM32F103C8 = true;
|
||||
#endif
|
||||
#if defined (MULTI_5IN1_INTERNAL)
|
||||
bool firmwareFlag_MULTI_5IN1_INTERNAL = true;
|
||||
#endif
|
||||
|
||||
// Channel order flags
|
||||
#if defined (AETR)
|
||||
bool firmwareFlag_ChannelOrder_AETR = true;
|
||||
#endif
|
||||
#if defined (AERT)
|
||||
bool firmwareFlag_ChannelOrder_AERT = true;
|
||||
#endif
|
||||
#if defined (ARET)
|
||||
bool firmwareFlag_ChannelOrder_ARET = true;
|
||||
#endif
|
||||
#if defined (ARTE)
|
||||
bool firmwareFlag_ChannelOrder_ARTE = true;
|
||||
#endif
|
||||
#if defined (ATRE)
|
||||
bool firmwareFlag_ChannelOrder_ATRE = true;
|
||||
#endif
|
||||
#if defined (ATER)
|
||||
bool firmwareFlag_ChannelOrder_ATER = true;
|
||||
#endif
|
||||
#if defined (EATR)
|
||||
bool firmwareFlag_ChannelOrder_EATR = true;
|
||||
#endif
|
||||
#if defined (EART)
|
||||
bool firmwareFlag_ChannelOrder_EART = true;
|
||||
#endif
|
||||
#if defined (ERAT)
|
||||
bool firmwareFlag_ChannelOrder_ERAT = true;
|
||||
#endif
|
||||
#if defined (ERTA)
|
||||
bool firmwareFlag_ChannelOrder_ERTA = true;
|
||||
#endif
|
||||
#if defined (ETRA)
|
||||
bool firmwareFlag_ChannelOrder_ETRA = true;
|
||||
#endif
|
||||
#if defined (ETAR)
|
||||
bool firmwareFlag_ChannelOrder_ETAR = true;
|
||||
#endif
|
||||
#if defined (TEAR)
|
||||
bool firmwareFlag_ChannelOrder_TEAR = true;
|
||||
#endif
|
||||
#if defined (TERA)
|
||||
bool firmwareFlag_ChannelOrder_TERA = true;
|
||||
#endif
|
||||
#if defined (TREA)
|
||||
bool firmwareFlag_ChannelOrder_TREA = true;
|
||||
#endif
|
||||
#if defined (TRAE)
|
||||
bool firmwareFlag_ChannelOrder_TRAE = true;
|
||||
#endif
|
||||
#if defined (TARE)
|
||||
bool firmwareFlag_ChannelOrder_TARE = true;
|
||||
#endif
|
||||
#if defined (TAER)
|
||||
bool firmwareFlag_ChannelOrder_TAER = true;
|
||||
#endif
|
||||
#if defined (RETA)
|
||||
bool firmwareFlag_ChannelOrder_RETA = true;
|
||||
#endif
|
||||
#if defined (REAT)
|
||||
bool firmwareFlag_ChannelOrder_REAT = true;
|
||||
#endif
|
||||
#if defined (RAET)
|
||||
bool firmwareFlag_ChannelOrder_RAET = true;
|
||||
#endif
|
||||
#if defined (RATE)
|
||||
bool firmwareFlag_ChannelOrder_RATE = true;
|
||||
#endif
|
||||
#if defined (RTAE)
|
||||
bool firmwareFlag_ChannelOrder_RTAE = true;
|
||||
#endif
|
||||
#if defined (RTEA)
|
||||
bool firmwareFlag_ChannelOrder_RTEA = true;
|
||||
#endif
|
||||
464
Multiprotocol/Bugs_a7105.ino
Normal file
464
Multiprotocol/Bugs_a7105.ino
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef BUGS_A7105_INO
|
||||
|
||||
//////////// rxid -> radioid algorithm //////////////////////////////
|
||||
// Hex digit 1 is periodic with length 2, and hex digit 2 is periodic
|
||||
// with length 16. However, storing the byte of those 2 digits
|
||||
// instead of manipulating bits results simpler code and smaller binary.
|
||||
const uint8_t PROGMEM BUGS_most_popular_67_cycle[]= {
|
||||
0x34, 0xc5, 0x6a, 0xb4, 0x29, 0xd5, 0x2c, 0xd3, 0x91, 0xb3, 0x6c, 0x49,
|
||||
0x52, 0x9c, 0x4d, 0x65, 0xc3, 0x4a, 0x5b, 0xd6, 0x92, 0x6d, 0x94, 0xa6,
|
||||
0x55, 0xcd, 0x2b, 0x9a, 0x36, 0x95, 0x4b, 0xd4, 0x35, 0x8d, 0x96, 0xb2,
|
||||
0xa3 };
|
||||
|
||||
static uint8_t __attribute__((unused)) BUGS_most_popular_67(uint8_t i)
|
||||
{
|
||||
uint8_t ii;
|
||||
if (i == 0)
|
||||
return 0xd2;
|
||||
else if (i == 1)
|
||||
return 0xda;
|
||||
else if (i % 16 < 2)
|
||||
{
|
||||
ii = 2 * (i / 16) + i % 16 - 2;
|
||||
if (ii % 2 == 0)
|
||||
ii += 7;
|
||||
}
|
||||
else
|
||||
ii=2 * (i / 16) + (i % 16 - 2) % 7;
|
||||
return pgm_read_byte_near( &BUGS_most_popular_67_cycle[ii]);
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) BUGS_most_popular_45(uint8_t i)
|
||||
{
|
||||
if (i == 0)
|
||||
return 0xa3;
|
||||
else if (i == 1)
|
||||
return 0x86;
|
||||
else
|
||||
{
|
||||
if (i % 8 == 1)
|
||||
i -= 8;
|
||||
else
|
||||
i--;
|
||||
return BUGS_most_popular_67(i);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) BUGS_most_popular_23(uint8_t i)
|
||||
{
|
||||
if (i == 0)
|
||||
return 0xb2;
|
||||
else if (i == 1)
|
||||
return 0xcb;
|
||||
else
|
||||
{
|
||||
if (i % 8 == 1)
|
||||
i -= 8;
|
||||
else
|
||||
i--;
|
||||
return BUGS_most_popular_45(i);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM BUGS_most_popular_01[] = {
|
||||
0x52, 0xac, 0x59, 0xa4, 0x53, 0xab, 0x57, 0xa9,
|
||||
0x56, 0xa5, 0x5b, 0xa7, 0x5d, 0xa6, 0x58, 0xad};
|
||||
|
||||
static uint32_t __attribute__((unused)) BUGS_most_popular(uint8_t i)
|
||||
{
|
||||
i += !(i <= 127);
|
||||
uint8_t mp01=pgm_read_byte_near( &BUGS_most_popular_01[i % 16] );
|
||||
return (uint32_t) mp01 << 24 |
|
||||
(uint32_t) BUGS_most_popular_23(i) << 16 |
|
||||
(uint32_t) BUGS_most_popular_45(i) << 8 |
|
||||
BUGS_most_popular_67(i);
|
||||
}
|
||||
|
||||
static uint32_t __attribute__((unused)) BUGS_second_most_popular(uint8_t i)
|
||||
{
|
||||
if (i < 127)
|
||||
return BUGS_most_popular(i + 1);
|
||||
else if (i > 128)
|
||||
return BUGS_most_popular(i - 1);
|
||||
else
|
||||
return 0x52d6926d;
|
||||
}
|
||||
|
||||
// The 22 irregular values do not match the above periodicities. They might be
|
||||
// errors from the readout, but let us try them here as long as it is not
|
||||
// proven.
|
||||
#define BUGS_NBR_IRREGULAR 22
|
||||
const uint16_t PROGMEM BUGS_irregular_keys[BUGS_NBR_IRREGULAR] = {
|
||||
1131, 1287, 2842, 4668, 5311, 11594, 13122, 13813,
|
||||
20655, 22975, 25007, 25068, 28252, 33309, 35364, 35765,
|
||||
37731, 40296, 43668, 46540, 49868, 65535 };
|
||||
|
||||
const uint32_t PROGMEM BUGS_irregular_values[BUGS_NBR_IRREGULAR] = {
|
||||
0x52d6926d, 0xa586da34, 0x5329d52c, 0xa66c4952,
|
||||
0x536c4952, 0x524a5bd6, 0x534d65c3, 0xa9d391b3,
|
||||
0x5249529c, 0xa555cd2b, 0xac9a3695, 0x58d391b3,
|
||||
0xa791b36c, 0x53926d94, 0xa7926d94, 0xa72cd391,
|
||||
0xa9b429d5, 0x5629d52c, 0xad2b9a36, 0xa74d65c3,
|
||||
0x526d94a6, 0xad96b2a3 };
|
||||
|
||||
static uint32_t __attribute__((unused)) BUGS_is_irregular(uint16_t i)
|
||||
{
|
||||
for (uint8_t j = 0; j < BUGS_NBR_IRREGULAR; ++j)
|
||||
if (pgm_read_word_near( &BUGS_irregular_keys[j]) == i)
|
||||
return pgm_read_dword_near( &BUGS_irregular_values[j]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t __attribute__((unused)) BUGS_rxid_to_radioid(uint16_t rxid)
|
||||
{
|
||||
uint8_t block = rxid / 256;
|
||||
uint8_t second_seq_size;
|
||||
bool use_most_popular;
|
||||
|
||||
if (rxid < 32768)
|
||||
{
|
||||
second_seq_size = 128 - block;
|
||||
use_most_popular = rxid % 256 >= second_seq_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
second_seq_size = block - 127;
|
||||
use_most_popular = 255 - rxid % 256 >= second_seq_size;
|
||||
}
|
||||
uint32_t v = BUGS_is_irregular(rxid);
|
||||
if (!v)
|
||||
{
|
||||
if (use_most_popular)
|
||||
v = BUGS_most_popular(rxid % 255);
|
||||
else
|
||||
v = BUGS_second_most_popular(rxid % 255);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
//////////// rxid -> radioid algorithm //////////////////////////////
|
||||
|
||||
// For code readability
|
||||
#define BUGS_CH_SW_ARM CH5_SW
|
||||
#define BUGS_CH_SW_ANGLE CH6_SW
|
||||
#define BUGS_CH_SW_FLIP CH7_SW
|
||||
#define BUGS_CH_SW_PICTURE CH8_SW
|
||||
#define BUGS_CH_SW_VIDEO CH9_SW
|
||||
#define BUGS_CH_SW_LED CH10_SW
|
||||
|
||||
// flags packet byte 4
|
||||
#define BUGS_FLAG_FLIP 0x08 // automatic flip
|
||||
#define BUGS_FLAG_MODE 0x04 // low/high speed select (set is high speed)
|
||||
#define BUGS_FLAG_VIDEO 0x02 // toggle video
|
||||
#define BUGS_FLAG_PICTURE 0x01 // toggle picture
|
||||
|
||||
// flags packet byte 5
|
||||
#define BUGS_FLAG_LED 0x80 // enable LEDs
|
||||
#define BUGS_FLAG_ARM 0x40 // arm (toggle to turn on motors)
|
||||
#define BUGS_FLAG_DISARM 0x20 // disarm (toggle to turn off motors)
|
||||
#define BUGS_FLAG_ANGLE 0x04 // angle/acro mode (set is angle mode)
|
||||
|
||||
#define BUGS_PACKET_SIZE 22
|
||||
#define BUGS_NUM_RFCHAN 16
|
||||
|
||||
enum {
|
||||
BUGS_BIND_1,
|
||||
BUGS_BIND_2,
|
||||
BUGS_BIND_3,
|
||||
BUGS_DATA_1,
|
||||
BUGS_DATA_2,
|
||||
BUGS_DATA_3,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) BUGS_check_arming()
|
||||
{
|
||||
uint8_t arm_channel = BUGS_CH_SW_ARM;
|
||||
|
||||
if (arm_channel != arm_channel_previous)
|
||||
{
|
||||
arm_channel_previous = arm_channel;
|
||||
if (arm_channel)
|
||||
{
|
||||
armed = 1;
|
||||
arm_flags ^= BUGS_FLAG_ARM;
|
||||
}
|
||||
else
|
||||
{
|
||||
armed = 0;
|
||||
arm_flags ^= BUGS_FLAG_DISARM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) BUGS_build_packet(uint8_t bind)
|
||||
{
|
||||
uint8_t force_values = bind | !armed;
|
||||
uint8_t change_channel = ((packet_count & 0x1) << 6);
|
||||
uint16_t aileron = convert_channel_16b_limit(AILERON,800,0);
|
||||
uint16_t elevator = convert_channel_16b_limit(ELEVATOR,800,0);
|
||||
uint16_t throttle = convert_channel_16b_limit(THROTTLE,0,800);
|
||||
uint16_t rudder = convert_channel_16b_limit(RUDDER,800,0);
|
||||
|
||||
memset(packet, 0, BUGS_PACKET_SIZE);
|
||||
packet[1] = 0x76; // txid (rx uses to know hopping frequencies)
|
||||
packet[2] = 0x71;
|
||||
packet[3] = 0x94;
|
||||
|
||||
BUGS_check_arming(); // sets globals arm_flags and armed
|
||||
if(bind)
|
||||
{
|
||||
packet[4] = change_channel | 0x80;
|
||||
packet[5] = 0x02 | arm_flags
|
||||
| GET_FLAG(BUGS_CH_SW_ANGLE, BUGS_FLAG_ANGLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[4] = change_channel | BUGS_FLAG_MODE
|
||||
| GET_FLAG(BUGS_CH_SW_FLIP, BUGS_FLAG_FLIP)
|
||||
| GET_FLAG(BUGS_CH_SW_PICTURE, BUGS_FLAG_PICTURE)
|
||||
| GET_FLAG(BUGS_CH_SW_VIDEO, BUGS_FLAG_VIDEO);
|
||||
packet[5] = 0x02 | arm_flags
|
||||
| GET_FLAG(BUGS_CH_SW_ANGLE, BUGS_FLAG_ANGLE)
|
||||
| GET_FLAG(BUGS_CH_SW_LED, BUGS_FLAG_LED);
|
||||
}
|
||||
|
||||
packet[6] = force_values ? 100 : (aileron >> 2);
|
||||
packet[7] = force_values ? 100 : (elevator >> 2);
|
||||
packet[8] = force_values ? 0 : (throttle >> 2);
|
||||
packet[9] = force_values ? 100 : (rudder >> 2);
|
||||
packet[10] = 100;
|
||||
packet[11] = 100;
|
||||
packet[12] = 100;
|
||||
packet[13] = 100;
|
||||
|
||||
packet[14] = ((aileron << 6) & 0xc0)
|
||||
| ((elevator << 4) & 0x30)
|
||||
| ((throttle << 2) & 0x0c)
|
||||
| ((rudder ) & 0x03);
|
||||
|
||||
// packet[15] = 0;
|
||||
|
||||
// driven trims
|
||||
packet[16] = aileron / 8 + 14;
|
||||
packet[17] = elevator / 8 + 14;
|
||||
packet[18] = 64;
|
||||
packet[19] = rudder / 8 + 14;
|
||||
|
||||
// packet[20] = 0;
|
||||
// packet[21] = 0;
|
||||
|
||||
uint8_t check = 0x6d;
|
||||
for (uint8_t i=1; i < BUGS_PACKET_SIZE; i++)
|
||||
check ^= packet[i];
|
||||
packet[0] = check;
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM BUGS_hop []= {
|
||||
0x1d, 0x3b, 0x4d, 0x29, 0x11, 0x2d, 0x0b, 0x3d, 0x59, 0x48, 0x17, 0x41, 0x23, 0x4e, 0x2a, 0x63, // bind phase ID=0xac59a453
|
||||
0x4b, 0x19, 0x35, 0x1e, 0x63, 0x0f, 0x45, 0x21, 0x51, 0x3a, 0x5d, 0x25, 0x0a, 0x44, 0x61, 0x27, // data phase ID=0xA4C56AB4 for txid 767194 if rx responds C6 BB 57 7F 00 00 00 00 00 00 FF 87 40 00 00 00
|
||||
};
|
||||
|
||||
static void __attribute__((unused))BUGS_set_radio_data()
|
||||
{ // captured radio data for bugs rx/tx version A2
|
||||
// it appears that the hopping frequencies are determined by the txid
|
||||
// and the data phase radio id is determined by the first 2 bytes of the
|
||||
// rx bind packet
|
||||
uint8_t offset=0;
|
||||
uint32_t radio_id=0xac59a453; // bind phase ID=0xac59a453
|
||||
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
offset=BUGS_NUM_RFCHAN;
|
||||
// Read radio_id from EEPROM
|
||||
uint8_t base_adr=BUGS_EEPROM_OFFSET+(RX_num&0x0F)*2;
|
||||
uint16_t rxid=0;
|
||||
for(uint8_t i=0; i<2; i++)
|
||||
rxid|=eeprom_read_byte((EE_ADDR)(base_adr+i))<<(i*8);
|
||||
radio_id = BUGS_rxid_to_radioid(rxid);
|
||||
}
|
||||
A7105_WriteID(radio_id);
|
||||
|
||||
for(uint8_t i=0; i<BUGS_NUM_RFCHAN;i++)
|
||||
hopping_frequency[i]=pgm_read_byte_near( &BUGS_hop[i+offset] );
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) BUGS_increment_counts()
|
||||
{ // this logic works with the use of packet_count in BUGS_build_packet
|
||||
// to properly indicate channel changes to rx
|
||||
packet_count += 1;
|
||||
if ((packet_count & 1) == 0)
|
||||
{
|
||||
hopping_frequency_no += 1;
|
||||
hopping_frequency_no %= BUGS_NUM_RFCHAN;
|
||||
}
|
||||
}
|
||||
|
||||
#define BUGS_PACKET_PERIOD 6100
|
||||
#define BUGS_DELAY_TX 2000
|
||||
#define BUGS_DELAY_POST_RX 1500
|
||||
#define BUGS_DELAY_BIND_RST 200
|
||||
|
||||
// FIFO config is one less than desired value
|
||||
#define BUGS_FIFO_SIZE_RX 15
|
||||
#define BUGS_FIFO_SIZE_TX 21
|
||||
uint16_t BUGS_callback(void)
|
||||
{
|
||||
uint8_t mode, base_adr;
|
||||
uint16_t rxid;
|
||||
uint16_t start;
|
||||
|
||||
// keep frequency tuning updated
|
||||
#ifndef FORCE_FLYSKY_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case BUGS_BIND_1:
|
||||
BUGS_build_packet(1);
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
A7105_WriteReg(A7105_03_FIFOI, BUGS_FIFO_SIZE_TX);
|
||||
A7105_WriteData(BUGS_PACKET_SIZE, hopping_frequency[hopping_frequency_no]);
|
||||
phase = BUGS_BIND_2;
|
||||
packet_period = BUGS_DELAY_TX;
|
||||
break;
|
||||
|
||||
case BUGS_BIND_2:
|
||||
//Wait for TX completion
|
||||
start=micros();
|
||||
while ((uint16_t)((uint16_t)micros()-start) < 500) // Wait max 500µs, using serial+telemetry exit in about 60µs
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
A7105_SetTxRxMode(RX_EN);
|
||||
A7105_WriteReg(A7105_0F_PLL_I, hopping_frequency[hopping_frequency_no] - 2);
|
||||
A7105_WriteReg(A7105_03_FIFOI, BUGS_FIFO_SIZE_RX);
|
||||
A7105_Strobe(A7105_RX);
|
||||
|
||||
BUGS_increment_counts();
|
||||
phase = BUGS_BIND_3;
|
||||
packet_period = BUGS_PACKET_PERIOD-BUGS_DELAY_TX-BUGS_DELAY_POST_RX;
|
||||
break;
|
||||
|
||||
case BUGS_BIND_3:
|
||||
mode = A7105_ReadReg(A7105_00_MODE);
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
A7105_SetTxRxMode(TX_EN);
|
||||
if (mode & 0x01)
|
||||
{
|
||||
phase = BUGS_BIND_1;
|
||||
packet_period = BUGS_DELAY_BIND_RST; // No received data so restart binding procedure.
|
||||
break;
|
||||
}
|
||||
A7105_ReadData(16);
|
||||
if ((packet[0] + packet[1] + packet[2] + packet[3]) == 0)
|
||||
{
|
||||
phase = BUGS_BIND_1;
|
||||
packet_period = BUGS_DELAY_BIND_RST; // No received data so restart binding procedure.
|
||||
break;
|
||||
}
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
BIND_DONE;
|
||||
// set radio_id
|
||||
rxid = (packet[1] << 8) + packet[2];
|
||||
base_adr=BUGS_EEPROM_OFFSET+(RX_num&0x0F)*2;
|
||||
for(uint8_t i=0; i<2; i++)
|
||||
eeprom_write_byte((EE_ADDR)(base_adr+i),rxid>>(i*8)); // Save rxid in EEPROM
|
||||
BUGS_set_radio_data();
|
||||
phase = BUGS_DATA_1;
|
||||
packet_count = 0;
|
||||
hopping_frequency_no = 0;
|
||||
packet_period = BUGS_DELAY_POST_RX;
|
||||
break;
|
||||
|
||||
case BUGS_DATA_1:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(BUGS_PACKET_PERIOD);
|
||||
#endif
|
||||
A7105_SetPower();
|
||||
BUGS_build_packet(0);
|
||||
A7105_WriteReg(A7105_03_FIFOI, BUGS_FIFO_SIZE_TX);
|
||||
A7105_WriteData(BUGS_PACKET_SIZE, hopping_frequency[hopping_frequency_no]);
|
||||
phase = BUGS_DATA_2;
|
||||
packet_period = BUGS_DELAY_TX;
|
||||
break;
|
||||
|
||||
case BUGS_DATA_2:
|
||||
//Wait for TX completion
|
||||
start=micros();
|
||||
while ((uint16_t)((uint16_t)micros()-start) < 500) // Wait max 500µs, using serial+telemetry exit in about 60µs
|
||||
if(!(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
A7105_SetTxRxMode(RX_EN);
|
||||
A7105_WriteReg(A7105_0F_PLL_I, hopping_frequency[hopping_frequency_no] - 2);
|
||||
A7105_WriteReg(A7105_03_FIFOI, BUGS_FIFO_SIZE_RX);
|
||||
A7105_Strobe(A7105_RX);
|
||||
|
||||
BUGS_increment_counts();
|
||||
phase = BUGS_DATA_3;
|
||||
packet_period = BUGS_PACKET_PERIOD-BUGS_DELAY_TX-BUGS_DELAY_POST_RX;
|
||||
break;
|
||||
|
||||
case BUGS_DATA_3:
|
||||
mode = A7105_ReadReg(A7105_00_MODE);
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
A7105_SetTxRxMode(TX_EN);
|
||||
if (!(mode & 0x01))
|
||||
{
|
||||
A7105_ReadData(16);
|
||||
#if defined(BUGS_HUB_TELEMETRY)
|
||||
v_lipo1=packet[10] == 0xff ? 0xff : 0x00; // Voltage in this case is only an alert on level good or bad.
|
||||
RX_RSSI=packet[3];
|
||||
// Read TX RSSI
|
||||
int16_t temp=256-(A7105_ReadReg(A7105_1D_RSSI_THOLD)*8)/5; // Value from A7105 is between 8 for maximum signal strength to 160 or less
|
||||
if(temp<0) temp=0;
|
||||
else if(temp>255) temp=255;
|
||||
TX_RSSI=temp;
|
||||
telemetry_link=1;
|
||||
#endif
|
||||
}
|
||||
phase = BUGS_DATA_1;
|
||||
packet_period = BUGS_DELAY_POST_RX;
|
||||
break;
|
||||
}
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void BUGS_init(void)
|
||||
{
|
||||
uint16_t rxid=0;
|
||||
uint8_t base_adr=BUGS_EEPROM_OFFSET+(RX_num&0x0F)*2;
|
||||
for(uint8_t i=0; i<2; i++)
|
||||
rxid|=eeprom_read_byte((EE_ADDR)(base_adr+i))<<(i*8);
|
||||
if(rxid==0xffff)
|
||||
BIND_IN_PROGRESS;
|
||||
|
||||
BUGS_set_radio_data();
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
phase = BUGS_BIND_1;
|
||||
else
|
||||
phase = BUGS_DATA_1;
|
||||
|
||||
A7105_Init();
|
||||
|
||||
hopping_frequency_no = 0;
|
||||
packet_count = 0;
|
||||
armed = 0;
|
||||
arm_flags = BUGS_FLAG_DISARM; // initial value from captures
|
||||
arm_channel_previous = BUGS_CH_SW_ARM;
|
||||
}
|
||||
|
||||
#endif
|
||||
409
Multiprotocol/CABELL_nrf224l01.ino
Normal file
409
Multiprotocol/CABELL_nrf224l01.ino
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
Protocol by Dennis Cabell, 2017
|
||||
KE8FZX
|
||||
|
||||
To use this software, you must adhere to the license terms described below, and assume all responsibility for the use
|
||||
of the software. The user is responsible for all consequences or damage that may result from using this software.
|
||||
The user is responsible for ensuring that the hardware used to run this software complies with local regulations and that
|
||||
any radio signal generated or received from use of this software is legal for that user to generate. The author(s) of this software
|
||||
assume no liability whatsoever. The author(s) of this software is not responsible for legal or civil consequences of
|
||||
using this software, including, but not limited to, any damages cause by lost control of a vehicle using this software.
|
||||
If this software is copied or modified, this disclaimer must accompany all copies.
|
||||
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// The Receiver for this protocol is available at: https://github.com/soligen2010/RC_RX_CABELL_V3_FHSS
|
||||
|
||||
#if defined(CABELL_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define CABELL_BIND_COUNT 2000 // At least 2000 so that if TX toggles the serial bind flag then bind mode is never exited
|
||||
#define CABELL_PACKET_PERIOD 3000 // Do not set too low or else next packet may not be finished transmitting before the channel is changed next time around
|
||||
|
||||
#define CABELL_NUM_CHANNELS 16 // The maximum number of RC channels that can be sent in one packet
|
||||
#define CABELL_MIN_CHANNELS 4 // The minimum number of channels that must be included in a packet, the number of channels cannot be reduced any further than this
|
||||
#define CABELL_PAYLOAD_BYTES 24 // 12 bits per value * 16 channels
|
||||
|
||||
#define CABELL_RADIO_CHANNELS 9 // This is 1/5 of the total number of radio channels used for FHSS
|
||||
#define CABELL_RADIO_MIN_CHANNEL_NUM 3 // Channel 0 is right on the boarder of allowed frequency range, so move up to avoid bleeding over
|
||||
#define CABELL_TELEMETRY_PACKET_LENGTH 4
|
||||
|
||||
#define CABELL_BIND_RADIO_ADDR 0xA4B7C123F7LL
|
||||
|
||||
#define CABELL_OPTION_MASK_CHANNEL_REDUCTION 0x0F
|
||||
#define CABELL_OPTION_MASK_RECIEVER_OUTPUT_MODE 0x30
|
||||
#define CABELL_OPTION_SHIFT_RECIEVER_OUTPUT_MODE 4
|
||||
#define CABELL_OPTION_MASK_MAX_POWER_OVERRIDE 0x40
|
||||
|
||||
typedef struct
|
||||
{
|
||||
enum RxMode_t : uint8_t
|
||||
{ // Note bit 8 is used to indicate if the packet is the first of 2 on the channel. Mask out this bit before using the enum
|
||||
normal = 0,
|
||||
bind = 1,
|
||||
setFailSafe = 2,
|
||||
normalWithTelemetry = 3,
|
||||
telemetryResponse = 4,
|
||||
unBind = 127
|
||||
} RxMode;
|
||||
uint8_t reserved = 0;
|
||||
uint8_t option;
|
||||
/* mask 0x0F : Channel reduction. The number of channels to not send (subtracted from the 16 max channels) at least 4 are always sent
|
||||
* mask 0x30>>4 : Receiver output mode
|
||||
* 0 (00) = Single PPM on individual pins for each channel
|
||||
* 1 (01) = SUM PPM on channel 1 pin
|
||||
* 2 (10) = Future use. Reserved for SBUS output
|
||||
* 3 (11) = Unused
|
||||
* mask 0x40>>6 Contains max power override flag for Multi-protocol TX module. Also sent to RX
|
||||
* mask 0x80>>7 Unused
|
||||
*/
|
||||
uint8_t modelNum;
|
||||
uint8_t checkSum_LSB;
|
||||
uint8_t checkSum_MSB;
|
||||
uint8_t payloadValue [CABELL_PAYLOAD_BYTES] = {0}; //12 bits per channel value, unsigned
|
||||
} CABELL_RxTxPacket_t;
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
static uint8_t __attribute__((unused)) CABELL_getNextChannel (uint8_t seqArray[], uint8_t seqArraySize, uint8_t prevChannel)
|
||||
{
|
||||
/* Possible channels are in 5 bands, each band comprised of seqArraySize channels
|
||||
* seqArray contains seqArraySize elements in the relative order in which we should progress through the band
|
||||
*
|
||||
* Each time the channel is changes, bands change in a way so that the next channel will be in a
|
||||
* different non-adjacent band. Both the band changes and the index in seqArray is incremented.
|
||||
*/
|
||||
prevChannel -= CABELL_RADIO_MIN_CHANNEL_NUM; // Subtract CABELL_RADIO_MIN_CHANNEL_NUM because it was added to the return value
|
||||
if(prevChannel>(seqArraySize * 5))
|
||||
prevChannel=seqArraySize * 5; // Constrain the values just in case something bogus was sent in.
|
||||
|
||||
uint8_t currBand = prevChannel / seqArraySize;
|
||||
uint8_t nextBand = (currBand + 3) % 5;
|
||||
|
||||
uint8_t prevChannalSeqArrayValue = prevChannel % seqArraySize;
|
||||
uint8_t prevChannalSeqArrayPosition = 0;
|
||||
for (int x = 0; x < seqArraySize; x++)
|
||||
{ // Find the position of the previous channel in the array
|
||||
if (seqArray[x] == prevChannalSeqArrayValue)
|
||||
prevChannalSeqArrayPosition = x;
|
||||
}
|
||||
uint8_t nextChannalSeqArrayPosition = prevChannalSeqArrayPosition + 1;
|
||||
if (nextChannalSeqArrayPosition >= seqArraySize)
|
||||
nextChannalSeqArrayPosition = 0;
|
||||
|
||||
return (seqArraySize * nextBand) + seqArray[nextChannalSeqArrayPosition] + CABELL_RADIO_MIN_CHANNEL_NUM; // Add CABELL_RADIO_MIN_CHANNEL_NUM so we dont use channel 0 as it may bleed below 2.400 GHz
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
#if defined CABELL_HUB_TELEMETRY
|
||||
static void __attribute__((unused)) CABELL_get_telemetry()
|
||||
{
|
||||
// calculate TX rssi based on past 250 expected telemetry packets. Cannot use full second count because telemetry_counter is not large enough
|
||||
state++;
|
||||
if (state > 250)
|
||||
{
|
||||
TX_RSSI = telemetry_counter;
|
||||
telemetry_counter = 0;
|
||||
state = 0;
|
||||
telemetry_lost=0;
|
||||
}
|
||||
|
||||
// Process incoming telemetry packet of it was received
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // data received from model
|
||||
NRF24L01_ReadPayload(packet, CABELL_TELEMETRY_PACKET_LENGTH);
|
||||
if ((packet[0] & 0x7F) == CABELL_RxTxPacket_t::telemetryResponse) // ignore high order bit in compare because it toggles with each packet
|
||||
{
|
||||
RX_RSSI = packet[1]; // Packet rate 0 to 255 where 255 is 100% packet rate
|
||||
v_lipo1 = packet[2]; // Directly from analog input of receiver, but reduced to 8-bit depth (0 to 255). Scaling depends on the input to the analog pin of the receiver.
|
||||
v_lipo2 = packet[3]; // Directly from analog input of receiver, but reduced to 8-bit depth (0 to 255). Scaling depends on the input to the analog pin of the receiver.
|
||||
telemetry_counter++;
|
||||
if(telemetry_lost==0)
|
||||
telemetry_link=1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no telemetry packet was received then delay by the typical telemetry packet processing time
|
||||
// This is done to try to keep the sendPacket process timing more consistent. Since the SPI payload read takes some time
|
||||
delayMicroseconds(50);
|
||||
}
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_FlushRx();
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) CABELL_send_packet(uint8_t bindMode)
|
||||
{
|
||||
#if defined CABELL_HUB_TELEMETRY
|
||||
if (!bindMode && (sub_protocol == CABELL_V3_TELEMETRY)) // check for incoming packet and switch radio back to TX mode if we were listening for telemetry
|
||||
CABELL_get_telemetry();
|
||||
#endif
|
||||
|
||||
CABELL_RxTxPacket_t TxPacket;
|
||||
|
||||
uint8_t channelReduction = constrain((option & CABELL_OPTION_MASK_CHANNEL_REDUCTION),0,CABELL_NUM_CHANNELS-CABELL_MIN_CHANNELS); // Max 12 - cannot reduce below 4 channels
|
||||
if (bindMode)
|
||||
channelReduction = 0; // Send full packet to bind as higher channels will contain bind info
|
||||
|
||||
uint8_t packetSize = sizeof(TxPacket) - ((((channelReduction - (channelReduction%2))/ 2)) * 3); // reduce 3 bytes per 2 channels, but not last channel if it is odd
|
||||
uint8_t maxPayloadValueIndex = sizeof(TxPacket.payloadValue) - (sizeof(TxPacket) - packetSize);
|
||||
|
||||
if ((sub_protocol == CABELL_UNBIND) && !bindMode)
|
||||
{
|
||||
TxPacket.RxMode = CABELL_RxTxPacket_t::unBind;
|
||||
TxPacket.option = option;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sub_protocol == CABELL_SET_FAIL_SAFE && !bindMode)
|
||||
TxPacket.RxMode = CABELL_RxTxPacket_t::setFailSafe;
|
||||
else
|
||||
{
|
||||
if (bindMode)
|
||||
TxPacket.RxMode = CABELL_RxTxPacket_t::bind;
|
||||
else
|
||||
{
|
||||
switch (sub_protocol)
|
||||
{
|
||||
case CABELL_V3_TELEMETRY:
|
||||
TxPacket.RxMode = CABELL_RxTxPacket_t::normalWithTelemetry;
|
||||
break;
|
||||
default:
|
||||
TxPacket.RxMode = CABELL_RxTxPacket_t::normal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
TxPacket.option = (bindMode) ? (option & (~CABELL_OPTION_MASK_CHANNEL_REDUCTION)) : option; //remove channel reduction if in bind mode
|
||||
}
|
||||
TxPacket.reserved = 0;
|
||||
TxPacket.modelNum = RX_num;
|
||||
uint16_t checkSum = TxPacket.modelNum + TxPacket.option + TxPacket.RxMode + TxPacket.reserved; // Start Calculate checksum
|
||||
|
||||
int adjusted_x;
|
||||
int payloadIndex = 0;
|
||||
uint16_t holdValue;
|
||||
|
||||
for (int x = 0;(x < CABELL_NUM_CHANNELS - channelReduction); x++)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case 0 : adjusted_x = ELEVATOR; break;
|
||||
case 1 : adjusted_x = AILERON; break;
|
||||
case 2 : adjusted_x = RUDDER; break;
|
||||
case 3 : adjusted_x = THROTTLE; break;
|
||||
default : adjusted_x = x; break;
|
||||
}
|
||||
holdValue = convert_channel_16b_limit(adjusted_x,1000,2000); // valid channel values are 1000 to 2000
|
||||
if (bindMode)
|
||||
{
|
||||
switch (adjusted_x)
|
||||
{
|
||||
case THROTTLE : holdValue = 1000; break; // always set throttle to off when binding for safety
|
||||
//tx address sent for bind
|
||||
case 11 : holdValue = 1000 + rx_tx_addr[0]; break;
|
||||
case 12 : holdValue = 1000 + rx_tx_addr[1]; break;
|
||||
case 13 : holdValue = 1000 + rx_tx_addr[2]; break;
|
||||
case 14 : holdValue = 1000 + rx_tx_addr[3]; break;
|
||||
case 15 : holdValue = 1000 + rx_tx_addr[4]; break;
|
||||
}
|
||||
}
|
||||
|
||||
// use 12 bits per value
|
||||
if (x % 2)
|
||||
{ //output channel number is ODD
|
||||
holdValue = holdValue<<4;
|
||||
payloadIndex--;
|
||||
}
|
||||
else
|
||||
holdValue &= 0x0FFF;
|
||||
TxPacket.payloadValue[payloadIndex] |= (uint8_t)(holdValue & 0x00FF);
|
||||
payloadIndex++;
|
||||
TxPacket.payloadValue[payloadIndex] |= (uint8_t)((holdValue>>8) & 0x00FF);
|
||||
payloadIndex++;
|
||||
}
|
||||
|
||||
for(int x = 0; x < maxPayloadValueIndex ; x++)
|
||||
checkSum += TxPacket.payloadValue[x]; // Finish Calculate checksum
|
||||
|
||||
TxPacket.checkSum_MSB = checkSum >> 8;
|
||||
TxPacket.checkSum_LSB = checkSum & 0x00FF;
|
||||
|
||||
// Set channel for next transmission
|
||||
rf_ch_num = CABELL_getNextChannel (hopping_frequency,CABELL_RADIO_CHANNELS, rf_ch_num);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH,rf_ch_num);
|
||||
|
||||
//NRF24L01_FlushTx(); //just in case things got hung up
|
||||
//NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(&TxPacket.RxMode);
|
||||
*p &= 0x7F; // Make sure 8th bit is clear
|
||||
*p |= (packet_count++)<<7; // This causes the 8th bit of the first byte to toggle with each xmit so consecutive payloads are not identical.
|
||||
// This is a work around for a reported bug in clone NRF24L01 chips that mis-took this case for a re-transmit of the same packet.
|
||||
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_WritePayload((uint8_t*)&TxPacket, packetSize);
|
||||
|
||||
#if defined CABELL_HUB_TELEMETRY
|
||||
if (!bindMode && (sub_protocol == CABELL_V3_TELEMETRY))
|
||||
{ // switch radio to rx as soon as packet is sent
|
||||
// calculate transmit time based on packet size and data rate of 1MB per sec
|
||||
// This is done because polling the status register during xmit caused issues.
|
||||
// bits = packst_size * 8 + 73 bits overhead
|
||||
// at 250 Kbs per sec, one bit is 4 uS
|
||||
// then add 140 uS which is 130 uS to begin the xmit and 10 uS fudge factor
|
||||
delayMicroseconds(((((unsigned long)packetSize * 8ul) + 73ul) * 4ul) + 140ul) ;
|
||||
packet_period = CABELL_PACKET_PERIOD + (constrain(((int16_t)(CABELL_NUM_CHANNELS - channelReduction) - (int16_t)6 ),(int16_t)0 ,(int16_t)10 ) * (int16_t)100); // increase packet period by 100 us for each channel over 6
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F); // RX mode with 16 bit CRC
|
||||
}
|
||||
else
|
||||
#endif
|
||||
packet_period = CABELL_PACKET_PERIOD; // Standard packet period when not in telemetry mode.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) CABELL_getChannelSequence (uint8_t outArray[], uint8_t numChannels, uint64_t permutation)
|
||||
{
|
||||
/* This procedure initializes an array with the sequence progression of channels.
|
||||
* This is not the actual channels itself, but the sequence base to be used within bands of
|
||||
* channels.
|
||||
*
|
||||
* There are numChannels! permutations for arranging the channels
|
||||
* one of these permutations will be calculated based on the permutation input
|
||||
* permutation should be between 1 and numChannels! but the routine will constrain it
|
||||
* if these bounds are exceeded. Typically the radio's unique TX ID should be used.
|
||||
*
|
||||
* The maximum numChannels is 20. Anything larger than this will cause the uint64_t
|
||||
* variables to overflow, yielding unknown results (possibly infinite loop?). Therefor
|
||||
* this routine constrains the value.
|
||||
*/
|
||||
uint8_t i; //iterator counts numChannels
|
||||
uint64_t indexOfNextSequenceValue;
|
||||
uint64_t numChannelsFactorial=1;
|
||||
uint8_t sequenceValue;
|
||||
|
||||
numChannels = constrain(numChannels,1,20);
|
||||
|
||||
for (i = 1; i <= numChannels;i++)
|
||||
{
|
||||
numChannelsFactorial *= i; // Calculate n!
|
||||
outArray[i-1] = i-1; // Initialize array with the sequence
|
||||
}
|
||||
|
||||
permutation = (permutation % numChannelsFactorial) + 1; // permutation must be between 1 and n! or this algorithm will infinite loop
|
||||
|
||||
//Rearrange the array elements based on the permutation selected
|
||||
for (i=0, permutation--; i<numChannels; i++ )
|
||||
{
|
||||
numChannelsFactorial /= ((uint64_t)numChannels)-i;
|
||||
indexOfNextSequenceValue = i+(permutation/numChannelsFactorial);
|
||||
permutation %= numChannelsFactorial;
|
||||
|
||||
//Copy the value in the selected array position
|
||||
sequenceValue = outArray[indexOfNextSequenceValue];
|
||||
|
||||
//Shift the unused elements in the array to make room to move in the one just selected
|
||||
for( ; indexOfNextSequenceValue > i; indexOfNextSequenceValue--)
|
||||
outArray[indexOfNextSequenceValue] = outArray[indexOfNextSequenceValue-1];
|
||||
|
||||
// Copy the selected value into it's new array slot
|
||||
outArray[i] = sequenceValue;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) CABELL_setAddress()
|
||||
{
|
||||
uint64_t CABELL_addr;
|
||||
|
||||
// Serial.print("NORM ID: ");Serial.print((uint32_t)(CABELL_normal_addr>>32)); Serial.print(" ");Serial.println((uint32_t)((CABELL_normal_addr<<32)>>32));
|
||||
|
||||
if (IS_BIND_DONE)
|
||||
{
|
||||
CABELL_addr = (((uint64_t)rx_tx_addr[0]) << 32) +
|
||||
(((uint64_t)rx_tx_addr[1]) << 24) +
|
||||
(((uint64_t)rx_tx_addr[2]) << 16) +
|
||||
(((uint64_t)rx_tx_addr[3]) << 8) +
|
||||
(((uint64_t)rx_tx_addr[4])); // Address to use after binding
|
||||
}
|
||||
else
|
||||
CABELL_addr = CABELL_BIND_RADIO_ADDR; // Static addr for binding
|
||||
|
||||
CABELL_getChannelSequence(hopping_frequency,CABELL_RADIO_CHANNELS,CABELL_addr); // Get the sequence for hopping through channels
|
||||
rf_ch_num = CABELL_RADIO_MIN_CHANNEL_NUM; // Initialize the channel sequence
|
||||
|
||||
packet_count=0;
|
||||
|
||||
uint64_t CABELL_Telemetry_addr = ~CABELL_addr; // Invert bits for reading so that telemetry packets have a different address.
|
||||
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, reinterpret_cast<uint8_t*>(&CABELL_Telemetry_addr), 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, reinterpret_cast<uint8_t*>(&CABELL_Telemetry_addr), 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, reinterpret_cast<uint8_t*>(&CABELL_addr), 5);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) CABELL_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // slower data rate gives better range/reliability
|
||||
CABELL_setAddress();
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 0x20); // 32 byte packet length
|
||||
NRF24L01_WriteReg(NRF24L01_12_RX_PW_P1, 0x20); // 32 byte packet length
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3F); // Enable dynamic payload length on all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x04); // Enable dynamic Payload Length
|
||||
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
uint16_t CABELL_callback()
|
||||
{
|
||||
if (IS_BIND_DONE)
|
||||
{
|
||||
CABELL_send_packet(0); // packet_period is set/adjusted in CABELL_send_packet
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
return packet_period;
|
||||
}
|
||||
else if (bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
CABELL_RF_init(); // non-bind address
|
||||
}
|
||||
else
|
||||
{
|
||||
CABELL_send_packet(1);
|
||||
bind_counter--;
|
||||
}
|
||||
return CABELL_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
void CABELL_init(void)
|
||||
{
|
||||
if (IS_BIND_DONE)
|
||||
bind_counter = 0;
|
||||
else
|
||||
bind_counter = CABELL_BIND_COUNT;
|
||||
CABELL_RF_init();
|
||||
|
||||
packet_period = CABELL_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,130 +18,113 @@
|
||||
//CC2500 SPI routines
|
||||
//-------------------------------
|
||||
//-------------------------------
|
||||
#ifdef CC2500_INSTALLED
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
void cc2500_readFifo(uint8_t *dpbuffer, uint8_t len)
|
||||
//----------------------------
|
||||
void CC2500_WriteReg(uint8_t address, uint8_t data)
|
||||
{
|
||||
ReadRegisterMulti(CC2500_3F_RXFIFO | CC2500_READ_BURST, dpbuffer, len);
|
||||
CC25_CSN_off;
|
||||
SPI_Write(address);
|
||||
NOP();
|
||||
SPI_Write(data);
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
//----------------------
|
||||
static void ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
|
||||
static void CC2500_ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
|
||||
{
|
||||
CC25_CSN_off;
|
||||
cc2500_spi_write(address);
|
||||
SPI_Write(CC2500_READ_BURST | address);
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
data[i] = cc2500_spi_read();
|
||||
data[i] = SPI_Read();
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
//*********************************************
|
||||
|
||||
void CC2500_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
|
||||
{
|
||||
CC25_CSN_off;
|
||||
cc2500_spi_write(CC2500_WRITE_BURST | address);
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
cc2500_spi_write(data[i]);
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
void cc2500_writeFifo(uint8_t *dpbuffer, uint8_t len)
|
||||
{
|
||||
cc2500_strobe(CC2500_SFTX);//0x3B
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, dpbuffer, len);
|
||||
cc2500_strobe(CC2500_STX);//0x35
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void cc2500_spi_write(uint8_t command) {
|
||||
uint8_t n=8;
|
||||
|
||||
SCK_off;//SCK start low
|
||||
SDI_off;
|
||||
while(n--)
|
||||
{
|
||||
if(command&0x80)
|
||||
SDI_on;
|
||||
else
|
||||
SDI_off;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
command = command << 1;
|
||||
}
|
||||
SDI_on;
|
||||
}
|
||||
|
||||
//----------------------------
|
||||
void cc2500_writeReg(uint8_t address, uint8_t data) {//same as 7105
|
||||
CC25_CSN_off;
|
||||
cc2500_spi_write(address);
|
||||
NOP();
|
||||
cc2500_spi_write(data);
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
uint8_t cc2500_spi_read(void)
|
||||
{
|
||||
uint8_t result;
|
||||
uint8_t i;
|
||||
result=0;
|
||||
for(i=0;i<8;i++)
|
||||
{
|
||||
if(SDO_1) ///
|
||||
result=(result<<1)|0x01;
|
||||
else
|
||||
result=result<<1;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
NOP();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------
|
||||
uint8_t cc2500_readReg(uint8_t address)
|
||||
static uint8_t CC2500_ReadReg(uint8_t address)
|
||||
{
|
||||
uint8_t result;
|
||||
CC25_CSN_off;
|
||||
address |=0x80; //bit 7 =1 for reading
|
||||
cc2500_spi_write(address);
|
||||
result = cc2500_spi_read();
|
||||
SPI_Write(CC2500_READ_SINGLE | address);
|
||||
result = SPI_Read();
|
||||
CC25_CSN_on;
|
||||
return(result);
|
||||
}
|
||||
|
||||
//------------------------
|
||||
void cc2500_strobe(uint8_t address)
|
||||
void CC2500_ReadData(uint8_t *dpbuffer, uint8_t len)
|
||||
{
|
||||
CC2500_ReadRegisterMulti(CC2500_3F_RXFIFO, dpbuffer, len);
|
||||
}
|
||||
|
||||
//*********************************************
|
||||
void CC2500_Strobe(uint8_t state)
|
||||
{
|
||||
CC25_CSN_off;
|
||||
cc2500_spi_write(address);
|
||||
SPI_Write(state);
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
static void CC2500_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
|
||||
{
|
||||
CC25_CSN_off;
|
||||
SPI_Write(CC2500_WRITE_BURST | address);
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
SPI_Write(data[i]);
|
||||
CC25_CSN_on;
|
||||
}
|
||||
|
||||
void CC2500_WriteData(uint8_t *dpbuffer, uint8_t len)
|
||||
{
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, dpbuffer, len);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
}
|
||||
|
||||
void CC2500_SetTxRxMode(uint8_t mode)
|
||||
{
|
||||
if(mode == TX_EN)
|
||||
{//from deviation firmware
|
||||
CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F);
|
||||
CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F | 0x40);
|
||||
}
|
||||
else
|
||||
if (mode == RX_EN)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F);
|
||||
CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F | 0x40);
|
||||
}
|
||||
else
|
||||
{
|
||||
CC2500_WriteReg(CC2500_02_IOCFG0, 0x2F);
|
||||
CC2500_WriteReg(CC2500_00_IOCFG2, 0x2F);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------
|
||||
void cc2500_resetChip(void)
|
||||
/*static void cc2500_resetChip(void)
|
||||
{
|
||||
// Toggle chip select signal
|
||||
CC25_CSN_on;
|
||||
_delay_us(30);
|
||||
delayMicroseconds(30);
|
||||
CC25_CSN_off;
|
||||
_delay_us(30);
|
||||
delayMicroseconds(30);
|
||||
CC25_CSN_on;
|
||||
_delay_us(45);
|
||||
cc2500_strobe(CC2500_SRES);
|
||||
delayMicroseconds(45);
|
||||
CC2500_Strobe(CC2500_SRES);
|
||||
_delay_ms(100);
|
||||
}
|
||||
|
||||
*/
|
||||
uint8_t CC2500_Reset()
|
||||
{
|
||||
cc2500_strobe(CC2500_SRES);
|
||||
_delay_us(1000);
|
||||
CC2500_Strobe(CC2500_SRES);
|
||||
delayMilliseconds(1);
|
||||
CC2500_SetTxRxMode(TXRX_OFF);
|
||||
return cc2500_readReg(CC2500_0E_FREQ1) == 0xC4;//check if reset
|
||||
return CC2500_ReadReg(CC2500_0E_FREQ1) == 0xC4;//check if reset
|
||||
}
|
||||
|
||||
void CC2500_SetPower_Value(uint8_t power)
|
||||
/*
|
||||
static void CC2500_SetPower_Value(uint8_t power)
|
||||
{
|
||||
const unsigned char patable[8]= {
|
||||
0xC5, // -12dbm
|
||||
@@ -154,35 +138,199 @@ void CC2500_SetPower_Value(uint8_t power)
|
||||
};
|
||||
if (power > 7)
|
||||
power = 7;
|
||||
cc2500_writeReg(CC2500_3E_PATABLE, patable[power]);
|
||||
CC2500_WriteReg(CC2500_3E_PATABLE, patable[power]);
|
||||
}
|
||||
|
||||
*/
|
||||
void CC2500_SetPower()
|
||||
{
|
||||
uint8_t power=CC2500_BIND_POWER;
|
||||
if(IS_BIND_DONE_on)
|
||||
if(IS_BIND_DONE)
|
||||
#ifdef CC2500_ENABLE_LOW_POWER
|
||||
power=IS_POWER_FLAG_on?CC2500_HIGH_POWER:CC2500_LOW_POWER;
|
||||
#else
|
||||
power=CC2500_HIGH_POWER;
|
||||
#endif
|
||||
if(IS_LBT_POWER_on)
|
||||
{
|
||||
power=CC2500_LBT_POWER;
|
||||
LBT_POWER_off; // Only accept once
|
||||
}
|
||||
if(IS_RANGE_FLAG_on)
|
||||
power=CC2500_RANGE_POWER;
|
||||
cc2500_writeReg(CC2500_3E_PATABLE, power);
|
||||
if(prev_power != power)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_3E_PATABLE, power);
|
||||
prev_power=power;
|
||||
}
|
||||
}
|
||||
|
||||
void CC2500_SetTxRxMode(uint8_t mode)
|
||||
void __attribute__((unused)) CC2500_SetFreqOffset()
|
||||
{
|
||||
if(mode == TX_EN)
|
||||
{//from deviation firmware
|
||||
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F | 0x40);
|
||||
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F);
|
||||
if(prev_option != option)
|
||||
{
|
||||
prev_option = option;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
}
|
||||
}
|
||||
|
||||
void __attribute__((unused)) CC2500_250K_Init()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
// Address Config = No address check
|
||||
// Base Frequency = 2400
|
||||
// CRC Autoflush = false
|
||||
// CRC Enable = false
|
||||
// Channel Spacing = 333.251953
|
||||
// Data Format = Normal mode
|
||||
// Data Rate = 249.939
|
||||
// Deviation = 126.953125
|
||||
// Device Address = 0
|
||||
// Manchester Enable = false
|
||||
// Modulated = true
|
||||
// Modulation Format = GFSK
|
||||
// Packet Length Mode = Variable packet length mode. Packet length configured by the first byte after sync word
|
||||
// RX Filter BW = 203.125000
|
||||
// Sync Word Qualifier Mode = No preamble/sync
|
||||
// TX Power = 0
|
||||
// Whitening = false
|
||||
// Fast Frequency Hopping - no PLL auto calibration
|
||||
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x01); // Packet Automation Control
|
||||
CC2500_WriteReg(CC2500_0B_FSCTRL1, 0x0A); // Frequency Synthesizer Control
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option); // Frequency offset hack
|
||||
CC2500_WriteReg(CC2500_0D_FREQ2, 0x5C); // Frequency Control Word, High Byte
|
||||
CC2500_WriteReg(CC2500_0E_FREQ1, 0x4E); // Frequency Control Word, Middle Byte
|
||||
CC2500_WriteReg(CC2500_0F_FREQ0, 0xC3); // Frequency Control Word, Low Byte
|
||||
CC2500_WriteReg(CC2500_10_MDMCFG4, 0x8D); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_11_MDMCFG3, 0x3B); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_12_MDMCFG2, 0x10); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_13_MDMCFG1, 0x23); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_14_MDMCFG0, 0xA4); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_15_DEVIATN, 0x62); // Modem Deviation Setting
|
||||
CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // Main Radio Control State Machine Configuration
|
||||
CC2500_WriteReg(CC2500_19_FOCCFG, 0x1D); // Frequency Offset Compensation Configuration
|
||||
CC2500_WriteReg(CC2500_1A_BSCFG, 0x1C); // Bit Synchronization Configuration
|
||||
CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0xC7); // AGC Control
|
||||
CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0x00); // AGC Control
|
||||
CC2500_WriteReg(CC2500_1D_AGCCTRL0, 0xB0); // AGC Control
|
||||
CC2500_WriteReg(CC2500_21_FREND1, 0xB6); // Front End RX Configuration
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0xEA); // Frequency Synthesizer Calibration
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, 0x00); // Frequency Synthesizer Calibration
|
||||
CC2500_WriteReg(CC2500_26_FSCAL0, 0x11); // Frequency Synthesizer Calibration
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
}
|
||||
void __attribute__((unused)) CC2500_250K_HoppingCalib(uint8_t num_freq)
|
||||
{
|
||||
for (uint8_t i = 0; i < num_freq; i++)
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
// spacing is 333.25 kHz, must multiply channel by 3
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[i]*3);
|
||||
// calibrate
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
delayMicroseconds(900);
|
||||
calData[i]=CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
}
|
||||
}
|
||||
void __attribute__((unused)) CC2500_250K_Hopping(uint8_t index)
|
||||
{
|
||||
// spacing is 333.25 kHz, must multiply channel by 3
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[index] * 3);
|
||||
// set PLL calibration
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[index]);
|
||||
}
|
||||
void __attribute__((unused)) CC2500_250K_RFChannel(uint8_t number)
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
// spacing is 333.25 kHz, must multiply channel by 3
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, number*3);
|
||||
// calibrate
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
delayMicroseconds(900);
|
||||
}
|
||||
|
||||
//NRF emulation layer with CRC16 enabled
|
||||
uint8_t cc2500_nrf_tx_addr[5], cc2500_nrf_addr_len;
|
||||
void __attribute__((unused)) CC2500_250K_NRF_SetTXAddr(uint8_t* addr, uint8_t len)
|
||||
{
|
||||
cc2500_nrf_addr_len = len;
|
||||
memcpy(cc2500_nrf_tx_addr, addr, len);
|
||||
}
|
||||
void __attribute__((unused)) CC2500_250K_NRF_WritePayload(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
#if defined(ESKY150V2_CC2500_INO)
|
||||
uint8_t buf[158];
|
||||
#else
|
||||
uint8_t buf[35];
|
||||
#endif
|
||||
uint8_t last = 0;
|
||||
uint8_t i;
|
||||
|
||||
//nrf preamble
|
||||
if(cc2500_nrf_tx_addr[cc2500_nrf_addr_len - 1] & 0x80)
|
||||
buf[0]=0xAA;
|
||||
else
|
||||
buf[0]=0x55;
|
||||
last++;
|
||||
// address
|
||||
for (i = 0; i < cc2500_nrf_addr_len; ++i)
|
||||
buf[last++] = cc2500_nrf_tx_addr[cc2500_nrf_addr_len - i - 1];
|
||||
// payload
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[last++] = msg[i];
|
||||
|
||||
// crc
|
||||
crc = 0xffff;
|
||||
for (uint8_t i = 1; i < last; ++i)
|
||||
crc16_update( buf[i], 8);
|
||||
buf[last++] = crc >> 8;
|
||||
buf[last++] = crc & 0xff;
|
||||
buf[last++] = 0;
|
||||
|
||||
//for(uint8_t i=0;i<last;i++)
|
||||
// debug("%02X ",buf[i]);
|
||||
//debugln("");
|
||||
// stop TX/RX
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
// flush tx FIFO
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
// packet length
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO, last);
|
||||
// transmit nrf packet
|
||||
uint8_t *buff=buf;
|
||||
uint8_t status;
|
||||
if(last>63)
|
||||
{
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, 63);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
last-=63;
|
||||
buff+=63;
|
||||
while(last)
|
||||
{//Loop until all the data is sent
|
||||
do
|
||||
{// Wait for the FIFO to become available
|
||||
status=CC2500_ReadReg(CC2500_3A_TXBYTES | CC2500_READ_BURST);
|
||||
}
|
||||
while((status&0x7F)>31 && (status&0x80)==0);
|
||||
if(last>31)
|
||||
{//Send 31 bytes
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, 31);
|
||||
last-=31;
|
||||
buff+=31;
|
||||
}
|
||||
else
|
||||
if (mode == RX_EN)
|
||||
{
|
||||
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F);
|
||||
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F | 0x40);
|
||||
{//Send last bytes
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last);
|
||||
last=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cc2500_writeReg(CC2500_02_IOCFG0, 0x2F);
|
||||
cc2500_writeReg(CC2500_00_IOCFG2, 0x2F);
|
||||
{//Send packet
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
845
Multiprotocol/CFlie_nrf24l01.ino
Normal file
845
Multiprotocol/CFlie_nrf24l01.ino
Normal file
@@ -0,0 +1,845 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Most of this code was ported from theseankelly's related DeviationTX work.
|
||||
|
||||
#if defined(CFLIE_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define CFLIE_BIND_COUNT 60
|
||||
|
||||
//=============================================================================
|
||||
// CRTP (Crazy RealTime Protocol) Implementation
|
||||
//=============================================================================
|
||||
|
||||
// Port IDs
|
||||
enum {
|
||||
CRTP_PORT_CONSOLE = 0x00,
|
||||
CRTP_PORT_PARAM = 0x02,
|
||||
CRTP_PORT_SETPOINT = 0x03,
|
||||
CRTP_PORT_MEM = 0x04,
|
||||
CRTP_PORT_LOG = 0x05,
|
||||
CRTP_PORT_POSITION = 0x06,
|
||||
CRTP_PORT_SETPOINT_GENERIC = 0x07,
|
||||
CRTP_PORT_PLATFORM = 0x0D,
|
||||
CRTP_PORT_LINK = 0x0F,
|
||||
};
|
||||
|
||||
// Channel definitions for the LOG port
|
||||
enum {
|
||||
CRTP_LOG_CHAN_TOC = 0x00,
|
||||
CRTP_LOG_CHAN_SETTINGS = 0x01,
|
||||
CRTP_LOG_CHAN_LOGDATA = 0x02,
|
||||
};
|
||||
|
||||
// Command definitions for the LOG port's TOC channel
|
||||
enum {
|
||||
CRTP_LOG_TOC_CMD_ELEMENT = 0x00,
|
||||
CRTP_LOG_TOC_CMD_INFO = 0x01,
|
||||
};
|
||||
|
||||
// Command definitions for the LOG port's CMD channel
|
||||
enum {
|
||||
CRTP_LOG_SETTINGS_CMD_CREATE_BLOCK = 0x00,
|
||||
CRTP_LOG_SETTINGS_CMD_APPEND_BLOCK = 0x01,
|
||||
CRTP_LOG_SETTINGS_CMD_DELETE_BLOCK = 0x02,
|
||||
CRTP_LOG_SETTINGS_CMD_START_LOGGING = 0x03,
|
||||
CRTP_LOG_SETTINGS_CMD_STOP_LOGGING = 0x04,
|
||||
CRTP_LOG_SETTINGS_CMD_RESET_LOGGING = 0x05,
|
||||
};
|
||||
|
||||
// Log variables types
|
||||
enum {
|
||||
LOG_UINT8 = 0x01,
|
||||
LOG_UINT16 = 0x02,
|
||||
LOG_UINT32 = 0x03,
|
||||
LOG_INT8 = 0x04,
|
||||
LOG_INT16 = 0x05,
|
||||
LOG_INT32 = 0x06,
|
||||
LOG_FLOAT = 0x07,
|
||||
LOG_FP16 = 0x08,
|
||||
};
|
||||
|
||||
#define CFLIE_TELEM_LOG_BLOCK_ID 0x01
|
||||
#define CFLIE_TELEM_LOG_BLOCK_PERIOD_10MS 50 // 50*10 = 500ms
|
||||
|
||||
// Setpoint type definitions for the generic setpoint channel
|
||||
enum {
|
||||
CRTP_SETPOINT_GENERIC_STOP_TYPE = 0x00,
|
||||
CRTP_SETPOINT_GENERIC_VELOCITY_WORLD_TYPE = 0x01,
|
||||
CRTP_SETPOINT_GENERIC_Z_DISTANCE_TYPE = 0x02,
|
||||
CRTP_SETPOINT_GENERIC_CPPM_EMU_TYPE = 0x03,
|
||||
};
|
||||
|
||||
static inline uint8_t crtp_create_header(uint8_t port, uint8_t channel)
|
||||
{
|
||||
return ((port)&0x0F)<<4 | (channel & 0x03);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// End CRTP implementation
|
||||
//=============================================================================
|
||||
|
||||
// Address size
|
||||
#define TX_ADDR_SIZE 5
|
||||
|
||||
// Timeout for callback in uSec, 10ms=10000us for Crazyflie
|
||||
#define CFLIE_PACKET_PERIOD 10000
|
||||
|
||||
#define MAX_PACKET_SIZE 32 // CRTP is 32 bytes
|
||||
|
||||
// CPPM CRTP supports up to 10 aux channels but deviation only
|
||||
// supports a total of 12 channels. R,P,Y,T leaves 8 aux channels left
|
||||
#define MAX_CPPM_AUX_CHANNELS 8
|
||||
|
||||
static uint8_t tx_payload_len = 0; // Length of the packet stored in packet
|
||||
static uint8_t rx_payload_len = 0; // Length of the packet stored in rx_packet
|
||||
static uint8_t rx_packet[MAX_PACKET_SIZE]; // For reading in ACK payloads
|
||||
|
||||
static uint8_t data_rate;
|
||||
|
||||
enum {
|
||||
CFLIE_INIT_SEARCH = 0,
|
||||
CFLIE_INIT_CRTP_LOG,
|
||||
CFLIE_INIT_DATA,
|
||||
CFLIE_SEARCH,
|
||||
CFLIE_DATA
|
||||
};
|
||||
|
||||
static uint8_t crtp_log_setup_state;
|
||||
enum {
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_INIT = 0,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_INFO,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_INFO,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_ITEM,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_ITEM,
|
||||
// It might be a good idea to add a state here
|
||||
// to send the command to reset the logging engine
|
||||
// to avoid log block ID conflicts. However, there
|
||||
// is not a conflict with the current defaults in
|
||||
// cfclient and I'd rather be able to log from the Tx
|
||||
// and cfclient simultaneously
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_CREATE_BLOCK,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_CREATE_BLOCK,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_START_BLOCK,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_START_BLOCK,
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_COMPLETE,
|
||||
};
|
||||
|
||||
// State variables for the crtp_log_setup_state_machine
|
||||
static uint8_t toc_size; // Size of the TOC read from the crazyflie
|
||||
static uint8_t next_toc_variable; // State variable keeping track of the next var to read
|
||||
static uint8_t vbat_var_id; // ID of the vbatMV variable
|
||||
static uint8_t extvbat_var_id; // ID of the extVbatMV variable
|
||||
static uint8_t rssi_var_id; // ID of the RSSI variable
|
||||
|
||||
// Constants used for finding var IDs from the toc
|
||||
static const char* pm_group_name = "pm";
|
||||
static const char* vbat_var_name = "vbatMV";
|
||||
static const uint8_t vbat_var_type = LOG_UINT16;
|
||||
static const char* extvbat_var_name = "extVbatMV";
|
||||
static const uint8_t extvbat_var_type = LOG_UINT16;
|
||||
static const char* radio_group_name = "radio";
|
||||
static const char* rssi_var_name = "rssi";
|
||||
static const uint8_t rssi_var_type = LOG_UINT8;
|
||||
|
||||
// Repurposing DSM Telemetry fields
|
||||
#define TELEM_CFLIE_INTERNAL_VBAT TELEM_DSM_FLOG_VOLT2 // Onboard voltage
|
||||
#define TELEM_CFLIE_EXTERNAL_VBAT TELEM_DSM_FLOG_VOLT1 // Voltage from external pin (BigQuad)
|
||||
#define TELEM_CFLIE_RSSI TELEM_DSM_FLOG_FADESA // Repurpose FADESA for RSSI
|
||||
|
||||
enum {
|
||||
PROTOOPTS_TELEMETRY = 0,
|
||||
PROTOOPTS_CRTP_MODE = 1,
|
||||
LAST_PROTO_OPT,
|
||||
};
|
||||
|
||||
#define TELEM_OFF 0
|
||||
#define TELEM_ON_ACKPKT 1
|
||||
#define TELEM_ON_CRTPLOG 2
|
||||
|
||||
#define CRTP_MODE_RPYT 0
|
||||
#define CRTP_MODE_CPPM 1
|
||||
|
||||
// Bit vector from bit position
|
||||
#define BV(bit) (1 << bit)
|
||||
|
||||
#define PACKET_CHKTIME 500 // time to wait if packet not yet acknowledged or timed out
|
||||
|
||||
// Helper for sending a packet
|
||||
// Assumes packet data has been put in packet
|
||||
// and tx_payload_len has been set correctly
|
||||
static void send_packet()
|
||||
{
|
||||
// clear packet status bits and Tx/Rx FIFOs
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT)));
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
|
||||
// Transmit the payload
|
||||
NRF24L01_WritePayload(packet, tx_payload_len);
|
||||
|
||||
// // Check and adjust transmission power.
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
static uint16_t dbg_cnt = 0;
|
||||
static uint8_t packet_ack()
|
||||
{
|
||||
if (++dbg_cnt > 50)
|
||||
{
|
||||
// debugln("S: %02x\n", NRF24L01_ReadReg(NRF24L01_07_STATUS));
|
||||
dbg_cnt = 0;
|
||||
}
|
||||
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT)))
|
||||
{
|
||||
case BV(NRF24L01_07_TX_DS):
|
||||
rx_payload_len = NRF24L01_GetDynamicPayloadSize();
|
||||
if (rx_payload_len > MAX_PACKET_SIZE)
|
||||
rx_payload_len = MAX_PACKET_SIZE;
|
||||
NRF24L01_ReadPayload(rx_packet, rx_payload_len);
|
||||
return PKT_ACKED;
|
||||
case BV(NRF24L01_07_MAX_RT):
|
||||
return PKT_TIMEOUT;
|
||||
}
|
||||
return PKT_PENDING;
|
||||
}
|
||||
|
||||
static void set_rate_channel(uint8_t rate, uint8_t channel)
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel); // Defined by model id
|
||||
NRF24L01_SetBitrate(rate); // Defined by model id
|
||||
}
|
||||
|
||||
static void send_search_packet()
|
||||
{
|
||||
uint8_t buf[1];
|
||||
buf[0] = 0xff;
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT)));
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
if (rf_ch_num++ > 125)
|
||||
{
|
||||
rf_ch_num = 0;
|
||||
switch(data_rate)
|
||||
{
|
||||
case NRF24L01_BR_250K:
|
||||
data_rate = NRF24L01_BR_1M;
|
||||
break;
|
||||
case NRF24L01_BR_1M:
|
||||
data_rate = NRF24L01_BR_2M;
|
||||
break;
|
||||
case NRF24L01_BR_2M:
|
||||
data_rate = NRF24L01_BR_250K;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_rate_channel(data_rate, rf_ch_num);
|
||||
|
||||
NRF24L01_WritePayload(buf, sizeof(buf));
|
||||
|
||||
}
|
||||
|
||||
// Frac 16.16
|
||||
#define FRAC_MANTISSA 16 // This means, not IEEE 754...
|
||||
#define FRAC_SCALE (1 << FRAC_MANTISSA)
|
||||
|
||||
// Convert fractional 16.16 to float32
|
||||
static void frac2float(int32_t n, float* res)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
*res = 0.0;
|
||||
return;
|
||||
}
|
||||
uint32_t m = n < 0 ? -n : n; // Figure out mantissa?
|
||||
int i;
|
||||
for (i = (31-FRAC_MANTISSA); (m & 0x80000000) == 0; i--, m <<= 1);
|
||||
m <<= 1; // Clear implicit leftmost 1
|
||||
m >>= 9;
|
||||
uint32_t e = 127 + i;
|
||||
if (n < 0) m |= 0x80000000;
|
||||
m |= e << 23;
|
||||
*((uint32_t *) res) = m;
|
||||
}
|
||||
|
||||
static void send_crtp_rpyt_packet()
|
||||
{
|
||||
int32_t f_roll;
|
||||
int32_t f_pitch;
|
||||
int32_t f_yaw;
|
||||
uint16_t thrust;
|
||||
|
||||
uint16_t val;
|
||||
|
||||
struct CommanderPacketRPYT
|
||||
{
|
||||
float roll;
|
||||
float pitch;
|
||||
float yaw;
|
||||
uint16_t thrust;
|
||||
}__attribute__((packed)) cpkt;
|
||||
|
||||
// Channels in AETR order
|
||||
// Roll, aka aileron, float +- 50.0 in degrees
|
||||
// float roll = -(float) Channels[0]*50.0/10000;
|
||||
val = convert_channel_16b_limit(AILERON, -10000, 10000);
|
||||
// f_roll = -Channels[0] * FRAC_SCALE / (10000 / 50);
|
||||
f_roll = val * FRAC_SCALE / (10000 / 50);
|
||||
|
||||
frac2float(f_roll, &cpkt.roll); // TODO: Remove this and use the correct Mode switch below...
|
||||
// debugln("Roll: raw, converted: %d, %d, %d, %0.2f", Channel_data[AILERON], val, f_roll, cpkt.roll);
|
||||
|
||||
// Pitch, aka elevator, float +- 50.0 degrees
|
||||
//float pitch = -(float) Channels[1]*50.0/10000;
|
||||
val = convert_channel_16b_limit(ELEVATOR, -10000, 10000);
|
||||
// f_pitch = -Channels[1] * FRAC_SCALE / (10000 / 50);
|
||||
f_pitch = -val * FRAC_SCALE / (10000 / 50);
|
||||
|
||||
frac2float(f_pitch, &cpkt.pitch); // TODO: Remove this and use the correct Mode switch below...
|
||||
// debugln("Pitch: raw, converted: %d, %d, %d, %0.2f", Channel_data[ELEVATOR], val, f_pitch, cpkt.pitch);
|
||||
|
||||
// Thrust, aka throttle 0..65535, working range 5535..65535
|
||||
// Android Crazyflie app puts out a throttle range of 0-80%: 0..52000
|
||||
thrust = convert_channel_16b_limit(THROTTLE, 0, 32767) * 2;
|
||||
|
||||
// Crazyflie needs zero thrust to unlock
|
||||
if (thrust < 900)
|
||||
cpkt.thrust = 0;
|
||||
else
|
||||
cpkt.thrust = thrust;
|
||||
|
||||
// debugln("Thrust: raw, converted: %d, %u, %u", Channel_data[THROTTLE], thrust, cpkt.thrust);
|
||||
|
||||
// Yaw, aka rudder, float +- 400.0 deg/s
|
||||
// float yaw = -(float) Channels[3]*400.0/10000;
|
||||
val = convert_channel_16b_limit(RUDDER, -10000, 10000);
|
||||
// f_yaw = - Channels[3] * FRAC_SCALE / (10000 / 400);
|
||||
f_yaw = val * FRAC_SCALE / (10000 / 400);
|
||||
frac2float(f_yaw, &cpkt.yaw);
|
||||
|
||||
// debugln("Yaw: raw, converted: %d, %d, %d, %0.2f", Channel_data[RUDDER], val, f_yaw, cpkt.yaw);
|
||||
|
||||
// Switch on/off?
|
||||
// TODO: Get X or + mode working again:
|
||||
// if (Channels[4] >= 0) {
|
||||
// frac2float(f_roll, &cpkt.roll);
|
||||
// frac2float(f_pitch, &cpkt.pitch);
|
||||
// } else {
|
||||
// // Rotate 45 degrees going from X to + mode or opposite.
|
||||
// // 181 / 256 = 0.70703125 ~= sqrt(2) / 2
|
||||
// int32_t f_x_roll = (f_roll + f_pitch) * 181 / 256;
|
||||
// frac2float(f_x_roll, &cpkt.roll);
|
||||
// int32_t f_x_pitch = (f_pitch - f_roll) * 181 / 256;
|
||||
// frac2float(f_x_pitch, &cpkt.pitch);
|
||||
// }
|
||||
|
||||
// Construct and send packet
|
||||
packet[0] = crtp_create_header(CRTP_PORT_SETPOINT, 0); // Commander packet to channel 0
|
||||
memcpy(&packet[1], (char*) &cpkt, sizeof(cpkt));
|
||||
tx_payload_len = 1 + sizeof(cpkt);
|
||||
send_packet();
|
||||
}
|
||||
|
||||
/*static void send_crtp_cppm_emu_packet()
|
||||
{
|
||||
struct CommanderPacketCppmEmu {
|
||||
struct {
|
||||
uint8_t numAuxChannels : 4; // Set to 0 through MAX_AUX_RC_CHANNELS
|
||||
uint8_t reserved : 4;
|
||||
} hdr;
|
||||
uint16_t channelRoll;
|
||||
uint16_t channelPitch;
|
||||
uint16_t channelYaw;
|
||||
uint16_t channelThrust;
|
||||
uint16_t channelAux[10];
|
||||
} __attribute__((packed)) cpkt;
|
||||
|
||||
// To emulate PWM RC signals, rescale channels from (-10000,10000) to (1000,2000)
|
||||
// This is done by dividing by 20 to get a total range of 1000 (-500,500)
|
||||
// and then adding 1500 to to rebase the offset
|
||||
#define RESCALE_RC_CHANNEL_TO_PWM(chan) ((chan / 20) + 1500)
|
||||
|
||||
// Make sure the number of aux channels in use is capped to MAX_CPPM_AUX_CHANNELS
|
||||
// uint8_t numAuxChannels = Model.num_channels - 4;
|
||||
uint8_t numAuxChannels = 2; // TODO: Figure this out correctly
|
||||
if(numAuxChannels > MAX_CPPM_AUX_CHANNELS)
|
||||
{
|
||||
numAuxChannels = MAX_CPPM_AUX_CHANNELS;
|
||||
}
|
||||
|
||||
cpkt.hdr.numAuxChannels = numAuxChannels;
|
||||
|
||||
// Remap AETR to AERT (RPYT)
|
||||
cpkt.channelRoll = convert_channel_16b_limit(AILERON,1000,2000);
|
||||
cpkt.channelPitch = convert_channel_16b_limit(ELEVATOR,1000,2000);
|
||||
// Note: T & R Swapped:
|
||||
cpkt.channelYaw = convert_channel_16b_limit(RUDDER, 1000, 2000);
|
||||
cpkt.channelThrust = convert_channel_16b_limit(THROTTLE, 1000, 2000);
|
||||
|
||||
// Rescale the rest of the aux channels - RC channel 4 and up
|
||||
for (uint8_t i = 4; i < 14; i++)
|
||||
{
|
||||
cpkt.channelAux[i] = convert_channel_16b_limit(i, 1000, 2000);
|
||||
}
|
||||
|
||||
// Total size of the commander packet is a 1-byte header, 4 2-byte channels and
|
||||
// a variable number of 2-byte auxiliary channels
|
||||
uint8_t commanderPacketSize = 1 + 8 + (2*numAuxChannels);
|
||||
|
||||
// Construct and send packet
|
||||
packet[0] = crtp_create_header(CRTP_PORT_SETPOINT_GENERIC, 0); // Generic setpoint packet to channel 0
|
||||
packet[1] = CRTP_SETPOINT_GENERIC_CPPM_EMU_TYPE;
|
||||
|
||||
// Copy the header (1) plus 4 2-byte channels (8) plus whatever number of 2-byte aux channels are in use
|
||||
memcpy(&packet[2], (char*)&cpkt, commanderPacketSize); // Why not use sizeof(cpkt) here??
|
||||
tx_payload_len = 2 + commanderPacketSize; // CRTP header, commander type, and packet
|
||||
send_packet();
|
||||
}*/
|
||||
|
||||
static void send_cmd_packet()
|
||||
{
|
||||
// TODO: Fix this so we can actually configure the packet type
|
||||
// switch(Model.proto_opts[PROTOOPTS_CRTP_MODE])
|
||||
// {
|
||||
// case CRTP_MODE_CPPM:
|
||||
// send_crtp_cppm_emu_packet();
|
||||
// break;
|
||||
// case CRTP_MODE_RPYT:
|
||||
// send_crtp_rpyt_packet();
|
||||
// break;
|
||||
// default:
|
||||
// send_crtp_rpyt_packet();
|
||||
// }
|
||||
|
||||
// send_crtp_cppm_emu_packet(); // oh maAAAn
|
||||
send_crtp_rpyt_packet();
|
||||
}
|
||||
|
||||
// State machine for setting up CRTP logging
|
||||
// returns 1 when the state machine has completed, 0 otherwise
|
||||
static uint8_t crtp_log_setup_state_machine()
|
||||
{
|
||||
uint8_t state_machine_completed = 0;
|
||||
// A note on the design of this state machine:
|
||||
//
|
||||
// Responses from the crazyflie come in the form of ACK payloads.
|
||||
// There is no retry logic associated with ACK payloads, so it is possible
|
||||
// to miss a response from the crazyflie. To avoid this, the request
|
||||
// packet must be re-sent until the expected response is received. However,
|
||||
// re-sending the same request generates another response in the crazyflie
|
||||
// Rx queue, which can produce large backlogs of duplicate responses.
|
||||
//
|
||||
// To avoid this backlog but still guard against dropped ACK payloads,
|
||||
// transmit cmd packets (which don't generate responses themselves)
|
||||
// until an empty ACK payload is received (the crazyflie alternates between
|
||||
// 0xF3 and 0xF7 for empty ACK payloads) which indicates the Rx queue on the
|
||||
// crazyflie has been drained. If the queue has been drained and the
|
||||
// desired ACK has still not been received, it was likely dropped and the
|
||||
// request should be re-transmit.
|
||||
|
||||
switch (crtp_log_setup_state) {
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_INIT:
|
||||
toc_size = 0;
|
||||
next_toc_variable = 0;
|
||||
vbat_var_id = 0;
|
||||
extvbat_var_id = 0;
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_INFO;
|
||||
// fallthrough
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_INFO:
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_INFO;
|
||||
packet[0] = crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_TOC);
|
||||
packet[1] = CRTP_LOG_TOC_CMD_INFO;
|
||||
tx_payload_len = 2;
|
||||
send_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_INFO:
|
||||
if (packet_ack() == PKT_ACKED) {
|
||||
if (rx_payload_len >= 3
|
||||
&& rx_packet[0] == crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_TOC)
|
||||
&& rx_packet[1] == CRTP_LOG_TOC_CMD_INFO) {
|
||||
// Received the ACK payload. Save the toc_size
|
||||
// and advance to the next state
|
||||
toc_size = rx_packet[2];
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_ITEM;
|
||||
return state_machine_completed;
|
||||
} else if (rx_packet[0] == 0xF3 || rx_packet[0] == 0xF7) {
|
||||
// "empty" ACK packet received - likely missed the ACK
|
||||
// payload we are waiting for.
|
||||
// return to the send state and retransmit the request
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_INFO;
|
||||
return state_machine_completed;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, send a cmd packet to get the next ACK in the Rx queue
|
||||
send_cmd_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_ITEM:
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_ITEM;
|
||||
packet[0] = crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_TOC);
|
||||
packet[1] = CRTP_LOG_TOC_CMD_ELEMENT;
|
||||
packet[2] = next_toc_variable;
|
||||
tx_payload_len = 3;
|
||||
send_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_ACK_CMD_GET_ITEM:
|
||||
if (packet_ack() == PKT_ACKED) {
|
||||
if (rx_payload_len >= 3
|
||||
&& rx_packet[0] == crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_TOC)
|
||||
&& rx_packet[1] == CRTP_LOG_TOC_CMD_ELEMENT
|
||||
&& rx_packet[2] == next_toc_variable) {
|
||||
// For every element in the TOC we must compare its
|
||||
// type (rx_packet[3]), group and name (back to back
|
||||
// null terminated strings starting with the fifth byte)
|
||||
// and see if it matches any of the variables we need
|
||||
// for logging
|
||||
//
|
||||
// Currently enabled for logging:
|
||||
// - vbatMV (LOG_UINT16)
|
||||
// - extVbatMV (LOG_UINT16)
|
||||
// - rssi (LOG_UINT8)
|
||||
if(rx_packet[3] == vbat_var_type
|
||||
&& (0 == strcmp((char*)&rx_packet[4], pm_group_name))
|
||||
&& (0 == strcmp((char*)&rx_packet[4 + strlen(pm_group_name) + 1], vbat_var_name))) {
|
||||
// Found the vbat element - save it for later
|
||||
vbat_var_id = next_toc_variable;
|
||||
}
|
||||
|
||||
if(rx_packet[3] == extvbat_var_type
|
||||
&& (0 == strcmp((char*)&rx_packet[4], pm_group_name))
|
||||
&& (0 == strcmp((char*)&rx_packet[4 + strlen(pm_group_name) + 1], extvbat_var_name))) {
|
||||
// Found the extvbat element - save it for later
|
||||
extvbat_var_id = next_toc_variable;
|
||||
}
|
||||
|
||||
if(rx_packet[3] == rssi_var_type
|
||||
&& (0 == strcmp((char*)&rx_packet[4], radio_group_name))
|
||||
&& (0 == strcmp((char*)&rx_packet[4 + strlen(radio_group_name) + 1], rssi_var_name))) {
|
||||
// Found the rssi element - save it for later
|
||||
rssi_var_id = next_toc_variable;
|
||||
}
|
||||
|
||||
// Advance the toc variable counter
|
||||
// If there are more variables, read them
|
||||
// If not, move on to the next state
|
||||
next_toc_variable += 1;
|
||||
if(next_toc_variable >= toc_size) {
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_CREATE_BLOCK;
|
||||
} else {
|
||||
// There are more TOC elements to get
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_ITEM;
|
||||
}
|
||||
return state_machine_completed;
|
||||
} else if (rx_packet[0] == 0xF3 || rx_packet[0] == 0xF7) {
|
||||
// "empty" ACK packet received - likely missed the ACK
|
||||
// payload we are waiting for.
|
||||
// return to the send state and retransmit the request
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CMD_GET_INFO;
|
||||
return state_machine_completed;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, send a cmd packet to get the next ACK in the Rx queue
|
||||
send_cmd_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_CREATE_BLOCK:
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_CREATE_BLOCK;
|
||||
packet[0] = crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_SETTINGS);
|
||||
packet[1] = CRTP_LOG_SETTINGS_CMD_CREATE_BLOCK;
|
||||
packet[2] = CFLIE_TELEM_LOG_BLOCK_ID; // Log block ID
|
||||
packet[3] = vbat_var_type; // Variable type
|
||||
packet[4] = vbat_var_id; // ID of the VBAT variable
|
||||
packet[5] = extvbat_var_type; // Variable type
|
||||
packet[6] = extvbat_var_id; // ID of the ExtVBat variable
|
||||
packet[7] = rssi_var_type; // Variable type
|
||||
packet[8] = rssi_var_id; // ID of the RSSI variable
|
||||
tx_payload_len = 9;
|
||||
send_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_CREATE_BLOCK:
|
||||
if (packet_ack() == PKT_ACKED) {
|
||||
if (rx_payload_len >= 2
|
||||
&& rx_packet[0] == crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_SETTINGS)
|
||||
&& rx_packet[1] == CRTP_LOG_SETTINGS_CMD_CREATE_BLOCK) {
|
||||
// Received the ACK payload. Advance to the next state
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_START_BLOCK;
|
||||
return state_machine_completed;
|
||||
} else if (rx_packet[0] == 0xF3 || rx_packet[0] == 0xF7) {
|
||||
// "empty" ACK packet received - likely missed the ACK
|
||||
// payload we are waiting for.
|
||||
// return to the send state and retransmit the request
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_CREATE_BLOCK;
|
||||
return state_machine_completed;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, send a cmd packet to get the next ACK in the Rx queue
|
||||
send_cmd_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_START_BLOCK:
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_START_BLOCK;
|
||||
packet[0] = crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_SETTINGS);
|
||||
packet[1] = CRTP_LOG_SETTINGS_CMD_START_LOGGING;
|
||||
packet[2] = CFLIE_TELEM_LOG_BLOCK_ID; // Log block ID 1
|
||||
packet[3] = CFLIE_TELEM_LOG_BLOCK_PERIOD_10MS; // Log frequency in 10ms units
|
||||
tx_payload_len = 4;
|
||||
send_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_ACK_CONTROL_START_BLOCK:
|
||||
if (packet_ack() == PKT_ACKED) {
|
||||
if (rx_payload_len >= 2
|
||||
&& rx_packet[0] == crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_SETTINGS)
|
||||
&& rx_packet[1] == CRTP_LOG_SETTINGS_CMD_START_LOGGING) {
|
||||
// Received the ACK payload. Advance to the next state
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_COMPLETE;
|
||||
return state_machine_completed;
|
||||
} else if (rx_packet[0] == 0xF3 || rx_packet[0] == 0xF7) {
|
||||
// "empty" ACK packet received - likely missed the ACK
|
||||
// payload we are waiting for.
|
||||
// return to the send state and retransmit the request
|
||||
crtp_log_setup_state =
|
||||
CFLIE_CRTP_LOG_SETUP_STATE_SEND_CONTROL_START_BLOCK;
|
||||
return state_machine_completed;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, send a cmd packet to get the next ACK in the Rx queue
|
||||
send_cmd_packet();
|
||||
break;
|
||||
|
||||
case CFLIE_CRTP_LOG_SETUP_STATE_COMPLETE:
|
||||
state_machine_completed = 1;
|
||||
return state_machine_completed;
|
||||
break;
|
||||
}
|
||||
|
||||
return state_machine_completed;
|
||||
}
|
||||
|
||||
static void CFLIE_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
// CRC, radio on
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x01); // Auto Acknowledgement for data pipe 0
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x13); // 3 retransmits, 500us delay
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num); // Defined in initialize_rx_tx_addr
|
||||
NRF24L01_SetBitrate(data_rate); // Defined in initialize_rx_tx_addr
|
||||
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, TX_ADDR_SIZE);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, TX_ADDR_SIZE);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x01); // Enable Dynamic Payload Length on pipe 0
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x06); // Enable Dynamic Payload Length, enable Payload with ACK
|
||||
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
// TODO: Fix telemetry
|
||||
|
||||
// Update telemetry using the CRTP logging framework
|
||||
// static void update_telemetry_crtplog()
|
||||
// {
|
||||
// static uint8_t frameloss = 0;
|
||||
|
||||
// // Read and reset count of dropped packets
|
||||
// frameloss += NRF24L01_ReadReg(NRF24L01_08_OBSERVE_TX) >> 4;
|
||||
// NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num); // reset packet loss counter
|
||||
// Telemetry.value[TELEM_DSM_FLOG_FRAMELOSS] = frameloss;
|
||||
// TELEMETRY_SetUpdated(TELEM_DSM_FLOG_FRAMELOSS);
|
||||
|
||||
// if (packet_ack() == PKT_ACKED) {
|
||||
// // See if the ACK packet is a cflie log packet
|
||||
// // A log data packet is a minimum of 5 bytes. Ignore anything less.
|
||||
// if (rx_payload_len >= 5) {
|
||||
// // Port 5 = log, Channel 2 = data
|
||||
// if (rx_packet[0] == crtp_create_header(CRTP_PORT_LOG, CRTP_LOG_CHAN_LOGDATA)) {
|
||||
// // The log block ID
|
||||
// if (rx_packet[1] == CFLIE_TELEM_LOG_BLOCK_ID) {
|
||||
// // Bytes 5 and 6 are the Vbat in mV units
|
||||
// uint16_t vBat;
|
||||
// memcpy(&vBat, &rx_packet[5], sizeof(uint16_t));
|
||||
// Telemetry.value[TELEM_CFLIE_INTERNAL_VBAT] = (int32_t) (vBat / 10); // The log value expects centivolts
|
||||
// TELEMETRY_SetUpdated(TELEM_CFLIE_INTERNAL_VBAT);
|
||||
|
||||
// // Bytes 7 and 8 are the ExtVbat in mV units
|
||||
// uint16_t extVBat;
|
||||
// memcpy(&extVBat, &rx_packet[7], sizeof(uint16_t));
|
||||
// Telemetry.value[TELEM_CFLIE_EXTERNAL_VBAT] = (int32_t) (extVBat / 10); // The log value expects centivolts
|
||||
// TELEMETRY_SetUpdated(TELEM_CFLIE_EXTERNAL_VBAT);
|
||||
|
||||
// // Byte 9 is the RSSI
|
||||
// Telemetry.value[TELEM_CFLIE_RSSI] = rx_packet[9];
|
||||
// TELEMETRY_SetUpdated(TELEM_CFLIE_RSSI);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Update telemetry using the ACK packet payload
|
||||
// static void update_telemetry_ackpkt()
|
||||
// {
|
||||
// static uint8_t frameloss = 0;
|
||||
|
||||
// // Read and reset count of dropped packets
|
||||
// frameloss += NRF24L01_ReadReg(NRF24L01_08_OBSERVE_TX) >> 4;
|
||||
// NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num); // reset packet loss counter
|
||||
// Telemetry.value[TELEM_DSM_FLOG_FRAMELOSS] = frameloss;
|
||||
// TELEMETRY_SetUpdated(TELEM_DSM_FLOG_FRAMELOSS);
|
||||
|
||||
// if (packet_ack() == PKT_ACKED) {
|
||||
// // Make sure this is an ACK packet (first byte will alternate between 0xF3 and 0xF7
|
||||
// if (rx_packet[0] == 0xF3 || rx_packet[0] == 0xF7) {
|
||||
// // If ACK packet contains RSSI (proper length and byte 1 is 0x01)
|
||||
// if(rx_payload_len >= 3 && rx_packet[1] == 0x01) {
|
||||
// Telemetry.value[TELEM_CFLIE_RSSI] = rx_packet[2];
|
||||
// TELEMETRY_SetUpdated(TELEM_CFLIE_RSSI);
|
||||
// }
|
||||
// // If ACK packet contains VBAT (proper length and byte 3 is 0x02)
|
||||
// if(rx_payload_len >= 8 && rx_packet[3] == 0x02) {
|
||||
// uint32_t vBat = 0;
|
||||
// memcpy(&vBat, &rx_packet[4], sizeof(uint32_t));
|
||||
// Telemetry.value[TELEM_CFLIE_INTERNAL_VBAT] = (int32_t)(vBat / 10); // The log value expects centivolts
|
||||
// TELEMETRY_SetUpdated(TELEM_CFLIE_INTERNAL_VBAT);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
static uint16_t CFLIE_callback()
|
||||
{
|
||||
switch (phase) {
|
||||
case CFLIE_INIT_SEARCH:
|
||||
send_search_packet();
|
||||
phase = CFLIE_SEARCH;
|
||||
break;
|
||||
case CFLIE_INIT_CRTP_LOG:
|
||||
if (crtp_log_setup_state_machine()) {
|
||||
phase = CFLIE_INIT_DATA;
|
||||
}
|
||||
break;
|
||||
case CFLIE_INIT_DATA:
|
||||
send_cmd_packet();
|
||||
phase = CFLIE_DATA;
|
||||
break;
|
||||
case CFLIE_SEARCH:
|
||||
switch (packet_ack()) {
|
||||
case PKT_PENDING:
|
||||
return PACKET_CHKTIME; // packet send not yet complete
|
||||
case PKT_ACKED:
|
||||
phase = CFLIE_DATA;
|
||||
// PROTOCOL_SetBindState(0);
|
||||
// MUSIC_Play(MUSIC_DONE_BINDING);
|
||||
BIND_DONE;
|
||||
break;
|
||||
case PKT_TIMEOUT:
|
||||
send_search_packet();
|
||||
}
|
||||
break;
|
||||
|
||||
case CFLIE_DATA:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(CFLIE_PACKET_PERIOD);
|
||||
#endif
|
||||
// if (Model.proto_opts[PROTOOPTS_TELEMETRY] == TELEM_ON_CRTPLOG) {
|
||||
// update_telemetry_crtplog();
|
||||
// } else if (Model.proto_opts[PROTOOPTS_TELEMETRY] == TELEM_ON_ACKPKT) {
|
||||
// update_telemetry_ackpkt();
|
||||
// }
|
||||
|
||||
if (packet_ack() == PKT_PENDING)
|
||||
return PACKET_CHKTIME; // packet send not yet complete
|
||||
send_cmd_packet();
|
||||
break;
|
||||
}
|
||||
return CFLIE_PACKET_PERIOD; // Packet at standard protocol interval
|
||||
}
|
||||
|
||||
// Generate address to use from TX id and manufacturer id (STM32 unique id)
|
||||
static uint8_t CFLIE_initialize_rx_tx_addr()
|
||||
{
|
||||
rx_tx_addr[0] =
|
||||
rx_tx_addr[1] =
|
||||
rx_tx_addr[2] =
|
||||
rx_tx_addr[3] =
|
||||
rx_tx_addr[4] = 0xE7; // CFlie uses fixed address
|
||||
|
||||
// if (Model.fixed_id) {
|
||||
// rf_ch_num = Model.fixed_id % 100;
|
||||
// switch (Model.fixed_id / 100) {
|
||||
// case 0:
|
||||
// data_rate = NRF24L01_BR_250K;
|
||||
// break;
|
||||
// case 1:
|
||||
// data_rate = NRF24L01_BR_1M;
|
||||
// break;
|
||||
// case 2:
|
||||
// data_rate = NRF24L01_BR_2M;
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (Model.proto_opts[PROTOOPTS_TELEMETRY] == TELEM_ON_CRTPLOG) {
|
||||
// return CFLIE_INIT_CRTP_LOG;
|
||||
// } else {
|
||||
// return CFLIE_INIT_DATA;
|
||||
// }
|
||||
// } else {
|
||||
// data_rate = NRF24L01_BR_250K;
|
||||
// rf_ch_num = 10;
|
||||
// return CFLIE_INIT_SEARCH;
|
||||
// }
|
||||
|
||||
// Default 1
|
||||
data_rate = NRF24L01_BR_1M;
|
||||
rf_ch_num = 10;
|
||||
|
||||
// Default 2
|
||||
// data_rate = NRF24L01_BR_2M;
|
||||
// rf_ch_num = 110;
|
||||
return CFLIE_INIT_SEARCH;
|
||||
}
|
||||
|
||||
void CFLIE_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
|
||||
phase = CFLIE_initialize_rx_tx_addr();
|
||||
crtp_log_setup_state = CFLIE_CRTP_LOG_SETUP_STATE_INIT;
|
||||
packet_count=0;
|
||||
|
||||
CFLIE_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,8 +12,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Merged CG023 and H8_3D protocols
|
||||
// compatible with EAchine 3D X4, CG023/CG031, Attop YD-822/YD-829/YD-829C and H8_3D/JJRC H20
|
||||
// compatible with EAchine 3D X4, CG023/CG031, Attop YD-822/YD-829/YD-829C and H8_3D/JJRC H20/H22
|
||||
|
||||
#if defined(CG023_NRF24L01_INO)
|
||||
|
||||
@@ -23,11 +22,8 @@
|
||||
#define CG023_INITIAL_WAIT 500
|
||||
#define CG023_PACKET_SIZE 15 // packets have 15-byte payload
|
||||
#define CG023_RF_BIND_CHANNEL 0x2D
|
||||
#define CG023_BIND_COUNT 1000 // 8 seconds
|
||||
#define CG023_BIND_COUNT 500 // 4 seconds
|
||||
#define YD829_PACKET_PERIOD 4100 // Timeout for callback in uSec
|
||||
#define H8_3D_PACKET_PERIOD 1800 // Timeout for callback in uSec
|
||||
#define H8_3D_PACKET_SIZE 20
|
||||
#define H8_3D_RF_NUM_CHANNELS 4
|
||||
|
||||
|
||||
enum CG023_FLAGS {
|
||||
@@ -53,91 +49,26 @@ enum YD829_FLAGS {
|
||||
YD829_FLAG_STILL = 0x80,
|
||||
};
|
||||
|
||||
enum H8_3D_FLAGS {
|
||||
// flags going to packet[17]
|
||||
H8_3D_FLAG_FLIP = 0x01,
|
||||
H8_3D_FLAG_RATE_MID = 0x02,
|
||||
H8_3D_FLAG_RATE_HIGH = 0x04,
|
||||
H8_3D_FLAG_HEADLESS = 0x10, // RTH + headless on H8, headless on JJRC H20
|
||||
H8_3D_FLAG_RTH = 0x40, // 360 flip mode on H8 3D, RTH on JJRC H20
|
||||
};
|
||||
|
||||
enum H8_3D_FLAGS_2 {
|
||||
// flags going to packet[18]
|
||||
H8_3D_FLAG_CALIBRATE = 0x20, // accelerometer calibration
|
||||
};
|
||||
|
||||
enum CG023_PHASES {
|
||||
CG023_BIND = 0,
|
||||
CG023_DATA
|
||||
};
|
||||
|
||||
void CG023_send_packet(uint8_t bind)
|
||||
static void __attribute__((unused)) CG023_send_packet()
|
||||
{
|
||||
// throttle : 0x00 - 0xFF
|
||||
throttle=convert_channel_8b(THROTTLE);
|
||||
// rudder
|
||||
rudder = convert_channel_8b_scale(RUDDER,0x44,0xBC); // yaw right : 0x80 (neutral) - 0xBC (right)
|
||||
rudder = convert_channel_16b_limit(RUDDER,0x44,0xBC); // yaw right : 0x80 (neutral) - 0xBC (right)
|
||||
if (rudder<=0x80)
|
||||
rudder=0x80-rudder; // yaw left : 0x00 (neutral) - 0x3C (left)
|
||||
// elevator : 0xBB - 0x7F - 0x43
|
||||
elevator = convert_channel_8b_scale(ELEVATOR, 0x43, 0xBB);
|
||||
elevator = convert_channel_16b_limit(ELEVATOR, 0x43, 0xBB);
|
||||
// aileron : 0x43 - 0x7F - 0xBB
|
||||
aileron = convert_channel_8b_scale(AILERON, 0x43, 0xBB);
|
||||
aileron = convert_channel_16b_limit(AILERON, 0x43, 0xBB);
|
||||
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
if(sub_protocol==H8_3D)
|
||||
{
|
||||
packet[0] = 0x13;
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
packet[8] = (rx_tx_addr[0]+rx_tx_addr[1]+rx_tx_addr[2]+rx_tx_addr[3]) & 0xff; // txid checksum
|
||||
memset(&packet[9], 0, 10);
|
||||
if (bind)
|
||||
{
|
||||
packet[5] = 0x00;
|
||||
packet[6] = 0x00;
|
||||
packet[7] = 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[5] = hopping_frequency_no;
|
||||
packet[6] = 0x08;
|
||||
packet[7] = 0x03;
|
||||
packet[9] = throttle;
|
||||
packet[10] = rudder;
|
||||
packet[11] = elevator;
|
||||
packet[12] = aileron;
|
||||
// neutral trims
|
||||
packet[13] = 0x20;
|
||||
packet[14] = 0x20;
|
||||
packet[15] = 0x20;
|
||||
packet[16] = 0x20;
|
||||
packet[17] = H8_3D_FLAG_RATE_HIGH;
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[17] |= H8_3D_FLAG_FLIP;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
packet[17] |= H8_3D_FLAG_HEADLESS;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
packet[17] |= H8_3D_FLAG_RTH; // 180/360 flip mode on H8 3D
|
||||
|
||||
// both sticks bottom left: calibrate acc
|
||||
if(packet[9] <= 0x05 && packet[10] >= 0xa7 && packet[11] <= 0x57 && packet[12] >= 0xa7)
|
||||
packet[18] = H8_3D_FLAG_CALIBRATE;
|
||||
}
|
||||
uint8_t sum = packet[9];
|
||||
for (uint8_t i=10; i < H8_3D_PACKET_SIZE-1; i++)
|
||||
sum += packet[i];
|
||||
packet[19] = sum; // data checksum
|
||||
}
|
||||
else
|
||||
{ // CG023 and YD829
|
||||
if (bind)
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
packet[0]= 0xaa;
|
||||
else
|
||||
packet[0]= 0x55;
|
||||
// transmitter id
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
// unknown
|
||||
packet[3] = 0x00;
|
||||
packet[4] = 0x00;
|
||||
@@ -154,141 +85,80 @@ void CG023_send_packet(uint8_t bind)
|
||||
if(sub_protocol==CG023)
|
||||
{
|
||||
// rate
|
||||
packet[13] = CG023_FLAG_RATE_HIGH;
|
||||
// flags
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[13] |= CG023_FLAG_FLIP;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
packet[13] |= CG023_FLAG_LED_OFF;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
packet[13] |= CG023_FLAG_STILL;
|
||||
if(Servo_data[AUX4] > PPM_SWITCH)
|
||||
packet[13] |= CG023_FLAG_VIDEO;
|
||||
if(Servo_data[AUX5] > PPM_SWITCH)
|
||||
packet[13] |= CG023_FLAG_EASY;
|
||||
packet[13] = CG023_FLAG_RATE_HIGH
|
||||
| GET_FLAG(CH5_SW,CG023_FLAG_FLIP)
|
||||
| GET_FLAG(CH6_SW,CG023_FLAG_LED_OFF)
|
||||
| GET_FLAG(CH7_SW,CG023_FLAG_STILL)
|
||||
| GET_FLAG(CH8_SW,CG023_FLAG_VIDEO)
|
||||
| GET_FLAG(CH9_SW,CG023_FLAG_EASY);
|
||||
}
|
||||
else
|
||||
{// YD829
|
||||
// rate
|
||||
packet[13] = YD829_FLAG_RATE_HIGH;
|
||||
// flags
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[13] |= YD829_FLAG_FLIP;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
packet[13] |= YD829_FLAG_STILL;
|
||||
if(Servo_data[AUX4] > PPM_SWITCH)
|
||||
packet[13] |= YD829_FLAG_VIDEO;
|
||||
if(Servo_data[AUX5] > PPM_SWITCH)
|
||||
packet[13] |= YD829_FLAG_HEADLESS;
|
||||
packet[13] = YD829_FLAG_RATE_HIGH
|
||||
| GET_FLAG(CH5_SW,YD829_FLAG_FLIP)
|
||||
| GET_FLAG(CH7_SW,YD829_FLAG_STILL)
|
||||
| GET_FLAG(CH8_SW,YD829_FLAG_VIDEO)
|
||||
| GET_FLAG(CH9_SW,YD829_FLAG_HEADLESS);
|
||||
}
|
||||
packet[14] = 0;
|
||||
}
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
|
||||
if (bind)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, sub_protocol==H8_3D?hopping_frequency[0]:CG023_RF_BIND_CHANNEL);
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CG023_RF_BIND_CHANNEL);
|
||||
else
|
||||
{
|
||||
if(sub_protocol==H8_3D)
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= H8_3D_RF_NUM_CHANNELS;
|
||||
}
|
||||
else // CG023 and YD829
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency_no);
|
||||
}
|
||||
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, sub_protocol==H8_3D ? H8_3D_PACKET_SIZE:CG023_PACKET_SIZE);
|
||||
XN297_WritePayload(packet, CG023_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
void CG023_init()
|
||||
static void __attribute__((unused)) CG023_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
if(sub_protocol==H8_3D)
|
||||
XN297_SetTXAddr((uint8_t *)"\xC4\x57\x09\x65\x21", 5);
|
||||
else // CG023 and YD829
|
||||
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
|
||||
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_SetPower();
|
||||
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
|
||||
}
|
||||
|
||||
uint16_t CG023_callback()
|
||||
{
|
||||
switch (phase)
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
case CG023_BIND:
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
phase = CG023_DATA;
|
||||
BIND_DONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
CG023_send_packet(1);
|
||||
bind_counter--;
|
||||
}
|
||||
break;
|
||||
case CG023_DATA:
|
||||
CG023_send_packet(0);
|
||||
break;
|
||||
}
|
||||
if(sub_protocol==CG023)
|
||||
return CG023_PACKET_PERIOD;
|
||||
else
|
||||
if(sub_protocol==YD829)
|
||||
return YD829_PACKET_PERIOD;
|
||||
return H8_3D_PACKET_PERIOD;
|
||||
CG023_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void CG023_initialize_txid()
|
||||
static void __attribute__((unused)) CG023_initialize_txid()
|
||||
{
|
||||
if(sub_protocol==H8_3D)
|
||||
{
|
||||
rx_tx_addr[0] = 0xa0 + (rx_tx_addr[0] % 0x10);
|
||||
rx_tx_addr[1] = 0xb0 + (rx_tx_addr[1] % 0x20);
|
||||
rx_tx_addr[2] = rx_tx_addr[2] % 0x20;
|
||||
rx_tx_addr[3] = rx_tx_addr[3] % 0x11;
|
||||
|
||||
hopping_frequency[0] = 0x06 + (((rx_tx_addr[0]>>8) + (rx_tx_addr[0]&0x0f)) % 0x0f);
|
||||
hopping_frequency[1] = 0x15 + (((rx_tx_addr[1]>>8) + (rx_tx_addr[1]&0x0f)) % 0x0f);
|
||||
hopping_frequency[2] = 0x24 + (((rx_tx_addr[2]>>8) + (rx_tx_addr[2]&0x0f)) % 0x0f);
|
||||
hopping_frequency[3] = 0x33 + (((rx_tx_addr[3]>>8) + (rx_tx_addr[3]&0x0f)) % 0x0f);
|
||||
}
|
||||
else
|
||||
{ // CG023 and YD829
|
||||
rx_tx_addr[0]= 0x80 | (rx_tx_addr[0] % 0x40);
|
||||
if( rx_tx_addr[0] == 0xAA) // avoid using same freq for bind and data channel
|
||||
rx_tx_addr[0] ++;
|
||||
hopping_frequency_no = rx_tx_addr[0] - 0x7D; // rf channel for data packets
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t initCG023(void)
|
||||
void CG023_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = CG023_BIND_COUNT;
|
||||
CG023_initialize_txid();
|
||||
CG023_init();
|
||||
phase=CG023_BIND;
|
||||
CG023_RF_init();
|
||||
if(sub_protocol==CG023)
|
||||
return CG023_INITIAL_WAIT+CG023_PACKET_PERIOD;
|
||||
else
|
||||
if(sub_protocol==YD829)
|
||||
return CG023_INITIAL_WAIT+YD829_PACKET_PERIOD;
|
||||
return CG023_INITIAL_WAIT+H8_3D_PACKET_PERIOD;
|
||||
packet_period=CG023_PACKET_PERIOD;
|
||||
else // YD829
|
||||
packet_period=YD829_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with Cheerson CX-10 blue & newer red pcb, CX-10A, CX11, CX-10 green pcb, DM007, Floureon FX-10, CX-Stars
|
||||
// compatible with Cheerson CX-10 blue & newer red pcb, CX-10A, CX11, CX-10 green pcb, DM007, Floureon FX-10, JXD 509 (Q282), Q222, Q242 and Q282
|
||||
// Last sync with hexfet new_protocols/cx10_nrf24l01.c dated 2015-11-26
|
||||
|
||||
#if defined(CX10_NRF24L01_INO)
|
||||
|
||||
@@ -21,10 +22,11 @@
|
||||
#define CX10_BIND_COUNT 4360 // 6 seconds
|
||||
#define CX10_PACKET_SIZE 15
|
||||
#define CX10A_PACKET_SIZE 19 // CX10 blue board packets have 19-byte payload
|
||||
#define Q2X2_PACKET_SIZE 21
|
||||
#define CX10_PACKET_PERIOD 1316 // Timeout for callback in uSec
|
||||
#define CX10A_PACKET_PERIOD 6000
|
||||
|
||||
#define INITIAL_WAIT 500
|
||||
#define CX10_INITIAL_WAIT 500
|
||||
|
||||
// flags
|
||||
#define CX10_FLAG_FLIP 0x10 // goes to rudder channel
|
||||
@@ -35,17 +37,16 @@
|
||||
#define CX10_FLAG_SNAPSHOT 0x04
|
||||
|
||||
// frequency channel management
|
||||
#define RF_BIND_CHANNEL 0x02
|
||||
#define NUM_RF_CHANNELS 4
|
||||
#define CX10_RF_BIND_CHANNEL 0x02
|
||||
#define CX10_NUM_RF_CHANNELS 4
|
||||
|
||||
enum {
|
||||
CX10_INIT1 = 0,
|
||||
CX10_BIND1,
|
||||
CX10_BIND1 = 0,
|
||||
CX10_BIND2,
|
||||
CX10_DATA
|
||||
};
|
||||
|
||||
void CX10_Write_Packet(uint8_t bind)
|
||||
static void __attribute__((unused)) CX10_Write_Packet(uint8_t bind)
|
||||
{
|
||||
uint8_t offset = 0;
|
||||
if(sub_protocol == CX10_BLUE)
|
||||
@@ -56,52 +57,109 @@ void CX10_Write_Packet(uint8_t bind)
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
// packet[5] to [8] (aircraft id) is filled during bind for blue board
|
||||
packet[5+offset] = lowByte(Servo_data[AILERON]);
|
||||
packet[6+offset]= highByte(Servo_data[AILERON]);
|
||||
packet[7+offset]= lowByte(Servo_data[ELEVATOR]);
|
||||
packet[8+offset]= highByte(Servo_data[ELEVATOR]);
|
||||
packet[9+offset]= lowByte(Servo_data[THROTTLE]);
|
||||
packet[10+offset]= highByte(Servo_data[THROTTLE]);
|
||||
packet[11+offset]= lowByte(Servo_data[RUDDER]);
|
||||
packet[12+offset]= highByte(Servo_data[RUDDER]);
|
||||
|
||||
uint16_t aileron= convert_channel_16b_limit(AILERON ,1000,2000);
|
||||
uint16_t elevator=convert_channel_16b_limit(ELEVATOR,2000,1000);
|
||||
uint16_t throttle=convert_channel_16b_limit(THROTTLE,1000,2000);
|
||||
uint16_t rudder= convert_channel_16b_limit(RUDDER ,2000,1000);
|
||||
// Channel 5 - flip flag
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[12+offset] |= CX10_FLAG_FLIP; // flip flag
|
||||
packet[12+offset] = GET_FLAG(CH5_SW,CX10_FLAG_FLIP); // flip flag applied on rudder
|
||||
|
||||
// Channel 6 - mode
|
||||
if(Servo_data[AUX2] > PPM_MAX_COMMAND) // mode 3 / headless on CX-10A
|
||||
packet[13+offset] = 0x02;
|
||||
// Channel 6 - rate mode is 2 lsb of packet 13
|
||||
if(CH6_SW) // rate 3 / headless on CX-10A
|
||||
flags = 0x02;
|
||||
else
|
||||
if(Servo_data[AUX2] < PPM_MIN_COMMAND)
|
||||
packet[13+offset] = 0x00; // mode 1
|
||||
if(Channel_data[CH6] < CHANNEL_MIN_COMMAND)
|
||||
flags = 0x00; // rate 1
|
||||
else
|
||||
packet[13+offset] = 0x01; // mode 2
|
||||
flags = 0x01; // rate 2
|
||||
uint8_t flags2=0; // packet 14
|
||||
|
||||
flags=0;
|
||||
if(sub_protocol == DM007)
|
||||
uint8_t video_state=packet[14] & 0x21;
|
||||
switch(sub_protocol)
|
||||
{
|
||||
// Channel 7 - snapshot
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
flags |= CX10_FLAG_SNAPSHOT;
|
||||
// Channel 8 - video
|
||||
if(Servo_data[AUX4] > PPM_SWITCH)
|
||||
flags |= CX10_FLAG_VIDEO;
|
||||
// Channel 9 - headless
|
||||
if(Servo_data[AUX5] > PPM_SWITCH)
|
||||
packet[13+offset] |= CX10_FLAG_HEADLESS;
|
||||
case CX10_BLUE:
|
||||
flags |= GET_FLAG(!CH7_SW, 0x10) // Channel 7 - picture
|
||||
|GET_FLAG( CH8_SW, 0x08); // Channel 8 - video
|
||||
break;
|
||||
case F_Q282:
|
||||
case F_Q242:
|
||||
case F_Q222:
|
||||
memcpy(&packet[15], "\x10\x10\xaa\xaa\x00\x00", 6);
|
||||
//FLIP|LED|PICTURE|VIDEO|HEADLESS|RTH|XCAL|YCAL
|
||||
flags2 = GET_FLAG(CH5_SW, 0x80) // Channel 5 - FLIP
|
||||
|GET_FLAG(!CH6_SW, 0x40) // Channel 6 - LED
|
||||
|GET_FLAG(CH9_SW, 0x08) // Channel 9 - HEADLESS
|
||||
|GET_FLAG(CH11_SW, 0x04) // Channel 11 - XCAL
|
||||
|GET_FLAG(CH12_SW, 0x02); // Channel 12 - YCAL or Start/Stop motors on JXD 509
|
||||
|
||||
if(sub_protocol==F_Q242)
|
||||
{
|
||||
flags=2;
|
||||
flags2|= GET_FLAG(CH7_SW,0x01) // Channel 7 - picture
|
||||
|GET_FLAG(CH8_SW,0x10); // Channel 8 - video
|
||||
packet[17]=0x00;
|
||||
packet[18]=0x00;
|
||||
}
|
||||
packet[14+offset] = flags;
|
||||
else
|
||||
{ // F_Q282 & F_Q222
|
||||
flags=3; // expert
|
||||
if(CH8_SW) // Channel 8 - F_Q282 video / F_Q222 Module 1
|
||||
{
|
||||
if (!(video_state & 0x20)) video_state ^= 0x21;
|
||||
}
|
||||
else
|
||||
if (video_state & 0x20) video_state &= 0x01;
|
||||
flags2 |= video_state
|
||||
|GET_FLAG(CH7_SW,0x10); // Channel 7 - F_Q282 picture / F_Q222 Module 2
|
||||
}
|
||||
if(CH10_SW) flags |=0x80; // Channel 10 - RTH
|
||||
break;
|
||||
case DM007:
|
||||
aileron = 3000 - aileron;
|
||||
//FLIP|MODE|PICTURE|VIDEO|HEADLESS
|
||||
flags2= GET_FLAG(CH7_SW,CX10_FLAG_SNAPSHOT) // Channel 7 - picture
|
||||
|GET_FLAG(CH8_SW,CX10_FLAG_VIDEO); // Channel 8 - video
|
||||
if(CH9_SW) flags |= CX10_FLAG_HEADLESS; // Channel 9 - headless
|
||||
break;
|
||||
case JC3015_2:
|
||||
aileron = 3000 - aileron;
|
||||
elevator = 3000 - elevator;
|
||||
//FLIP|MODE|LED|DFLIP
|
||||
if(CH8_SW) packet[12] &= ~CX10_FLAG_FLIP;
|
||||
case JC3015_1:
|
||||
//FLIP|MODE|PICTURE|VIDEO
|
||||
flags2= GET_FLAG(CH7_SW,_BV(3)) // Channel 7
|
||||
|GET_FLAG(CH8_SW,_BV(4)); // Channel 8
|
||||
break;
|
||||
case MK33041:
|
||||
elevator = 3000 - elevator;
|
||||
//FLIP|MODE|PICTURE|VIDEO|HEADLESS|RTH
|
||||
flags|=GET_FLAG(CH7_SW,_BV(7)) // Channel 7 - picture
|
||||
|GET_FLAG(CH10_SW,_BV(2)); // Channel 10 - rth
|
||||
flags2=GET_FLAG(CH8_SW,_BV(0)) // Channel 8 - video
|
||||
|GET_FLAG(CH9_SW,_BV(5)); // Channel 9 - headless
|
||||
break;
|
||||
}
|
||||
packet[5+offset] = lowByte(aileron);
|
||||
packet[6+offset] = highByte(aileron);
|
||||
packet[7+offset] = lowByte(elevator);
|
||||
packet[8+offset] = highByte(elevator);
|
||||
packet[9+offset] = lowByte(throttle);
|
||||
packet[10+offset]= highByte(throttle);
|
||||
packet[11+offset]= lowByte(rudder);
|
||||
packet[12+offset]|= highByte(rudder);
|
||||
packet[13+offset]=flags;
|
||||
packet[14+offset]=flags2;
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP));
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
if (bind)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CX10_RF_BIND_CHANNEL);
|
||||
else
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= NUM_RF_CHANNELS;
|
||||
hopping_frequency_no %= CX10_NUM_RF_CHANNELS;
|
||||
}
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
@@ -111,28 +169,19 @@ void CX10_Write_Packet(uint8_t bind)
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
void CX10_init()
|
||||
static void __attribute__((unused)) CX10_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
|
||||
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
|
||||
XN297_SetRXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgment on all data pipes
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, packet_length); // rx pipe 0 (used only for blue board)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, RF_BIND_CHANNEL);
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CX10_RF_BIND_CHANNEL);
|
||||
}
|
||||
|
||||
uint16_t CX10_callback() {
|
||||
uint16_t CX10_callback()
|
||||
{
|
||||
switch (phase) {
|
||||
case CX10_INIT1:
|
||||
phase = bind_phase;
|
||||
break;
|
||||
case CX10_BIND1:
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
@@ -146,69 +195,95 @@ uint16_t CX10_callback() {
|
||||
}
|
||||
break;
|
||||
case CX10_BIND2:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & BV(NRF24L01_07_RX_DR))
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
XN297_ReadPayload(packet, packet_length);
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
if(packet[9] == 1)
|
||||
phase = CX10_BIND1;
|
||||
{
|
||||
BIND_DONE;
|
||||
phase = CX10_DATA;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// switch to TX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
CX10_Write_Packet(1);
|
||||
delay(1); // used to be 300µs in deviation but not working so 1ms now
|
||||
// wait for packet to be sent
|
||||
while( (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS)) == 0); //delayMicroseconds(400);
|
||||
// switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
XN297_Configure(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO) | BV(NRF24L01_00_PWR_UP) | BV(NRF24L01_00_PRIM_RX));
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
|
||||
}
|
||||
break;
|
||||
case CX10_DATA:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
CX10_Write_Packet(0);
|
||||
break;
|
||||
}
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void initialize_txid()
|
||||
static void __attribute__((unused)) CX10_initialize_txid()
|
||||
{
|
||||
rx_tx_addr[1]%= 0x30;
|
||||
if(sub_protocol&0x08) //F_Q2X2 protocols
|
||||
{
|
||||
uint8_t offset=0; //F_Q282
|
||||
if(sub_protocol==F_Q242)
|
||||
offset=2;
|
||||
if(sub_protocol==F_Q222)
|
||||
offset=3;
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
hopping_frequency[i]=0x46+2*i+offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
hopping_frequency[0] = 0x03 + (rx_tx_addr[0] & 0x0F);
|
||||
hopping_frequency[1] = 0x16 + (rx_tx_addr[0] >> 4);
|
||||
hopping_frequency[2] = 0x2D + (rx_tx_addr[1] & 0x0F);
|
||||
hopping_frequency[3] = 0x40 + (rx_tx_addr[1] >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t initCX10(void)
|
||||
void CX10_init(void)
|
||||
{
|
||||
switch(sub_protocol)
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
|
||||
if(protocol == PROTO_Q2X2)
|
||||
sub_protocol|=0x08; // Increase the number of sub_protocols for CX-10
|
||||
|
||||
if(sub_protocol==CX10_BLUE)
|
||||
{
|
||||
case CX10_GREEN:
|
||||
case DM007:
|
||||
packet_length = CX10_PACKET_SIZE;
|
||||
packet_period = CX10_PACKET_PERIOD;
|
||||
bind_phase = CX10_BIND1;
|
||||
bind_counter = CX10_BIND_COUNT;
|
||||
break;
|
||||
case CX10_BLUE:
|
||||
packet_length = CX10A_PACKET_SIZE;
|
||||
packet_period = CX10A_PACKET_PERIOD;
|
||||
bind_phase = CX10_BIND2;
|
||||
bind_counter=0;
|
||||
|
||||
phase = CX10_BIND2;
|
||||
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
packet[5+i] = 0xff; // clear aircraft id
|
||||
packet[9] = 0;
|
||||
break;
|
||||
}
|
||||
initialize_txid();
|
||||
CX10_init();
|
||||
phase = CX10_INIT1;
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
return INITIAL_WAIT;
|
||||
else
|
||||
{
|
||||
if(sub_protocol&0x08) //F_Q2X2 protocols
|
||||
packet_length = Q2X2_PACKET_SIZE;
|
||||
else
|
||||
packet_length = CX10_PACKET_SIZE;
|
||||
packet_period = CX10_PACKET_PERIOD;
|
||||
phase = CX10_BIND1;
|
||||
bind_counter = CX10_BIND_COUNT;
|
||||
}
|
||||
CX10_initialize_txid();
|
||||
CX10_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,72 +12,36 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef CYRF6936_INSTALLED
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
void cyrf_spi_write(uint8_t command)
|
||||
{
|
||||
uint8_t n=8;
|
||||
SCK_off;//SCK start low
|
||||
SDI_off;
|
||||
while(n--) {
|
||||
if(command&0x80)
|
||||
SDI_on;
|
||||
else
|
||||
SDI_off;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
command = command << 1;
|
||||
}
|
||||
SDI_on;
|
||||
}
|
||||
|
||||
uint8_t cyrf_spi_read()
|
||||
{
|
||||
uint8_t result;
|
||||
uint8_t i;
|
||||
result=0;
|
||||
for(i=0;i<8;i++)
|
||||
{
|
||||
if(SDO_1) ///
|
||||
result=(result<<1)|0x01;
|
||||
else
|
||||
result=result<<1;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
NOP();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CYRF_WriteRegister(uint8_t address, uint8_t data)
|
||||
{
|
||||
CYRF_CSN_off;
|
||||
cyrf_spi_write(0x80 | address);
|
||||
cyrf_spi_write(data);
|
||||
SPI_Write(0x80 | address);
|
||||
SPI_Write(data);
|
||||
CYRF_CSN_on;
|
||||
}
|
||||
|
||||
void CYRF_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
|
||||
static void CYRF_WriteRegisterMulti(uint8_t address, const uint8_t data[], uint8_t length)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
CYRF_CSN_off;
|
||||
cyrf_spi_write(0x80 | address);
|
||||
SPI_Write(0x80 | address);
|
||||
for(i = 0; i < length; i++)
|
||||
cyrf_spi_write(data[i]);
|
||||
SPI_Write(data[i]);
|
||||
CYRF_CSN_on;
|
||||
}
|
||||
|
||||
void CYRF_ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
|
||||
static void CYRF_ReadRegisterMulti(uint8_t address, uint8_t data[], uint8_t length)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
CYRF_CSN_off;
|
||||
cyrf_spi_write(address);
|
||||
SPI_Write(address);
|
||||
for(i = 0; i < length; i++)
|
||||
data[i] = cyrf_spi_read();
|
||||
data[i] = SPI_Read();
|
||||
CYRF_CSN_on;
|
||||
}
|
||||
|
||||
@@ -85,8 +49,8 @@ uint8_t CYRF_ReadRegister(uint8_t address)
|
||||
{
|
||||
uint8_t data;
|
||||
CYRF_CSN_off;
|
||||
cyrf_spi_write(address);
|
||||
data = cyrf_spi_read();
|
||||
SPI_Write(address);
|
||||
data = SPI_Read();
|
||||
CYRF_CSN_on;
|
||||
return data;
|
||||
}
|
||||
@@ -94,17 +58,19 @@ uint8_t CYRF_ReadRegister(uint8_t address)
|
||||
|
||||
uint8_t CYRF_Reset()
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x01);//software reset
|
||||
_delay_us(200);//
|
||||
// RS_HI;
|
||||
// _delay_us(100);
|
||||
// RS_LO;
|
||||
// _delay_us(100);
|
||||
#ifdef CYRF_RST_HI
|
||||
CYRF_RST_HI; //Hardware reset
|
||||
delayMicroseconds(100);
|
||||
CYRF_RST_LO;
|
||||
delayMicroseconds(100);
|
||||
#endif
|
||||
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x01); //Software reset
|
||||
delayMicroseconds(200);
|
||||
CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0); //Enable XOUT as GPIO
|
||||
CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04); //Enable PACTL as GPIO
|
||||
CYRF_SetTxRxMode(TXRX_OFF);
|
||||
//Verify the CYRD chip is responding
|
||||
return (CYRF_ReadRegister(CYRF_10_FRAMING_CFG) == 0xa5);//return if reset
|
||||
//Verify the CYRF chip is responding
|
||||
return (CYRF_ReadRegister(CYRF_10_FRAMING_CFG) == 0xa5);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,6 +78,7 @@ uint8_t CYRF_Reset()
|
||||
*/
|
||||
void CYRF_GetMfgData(uint8_t data[])
|
||||
{
|
||||
#ifndef FORCE_CYRF_ID
|
||||
/* Fuses power on */
|
||||
CYRF_WriteRegister(CYRF_25_MFG_ID, 0xFF);
|
||||
|
||||
@@ -119,6 +86,9 @@ void CYRF_GetMfgData(uint8_t data[])
|
||||
|
||||
/* Fuses power off */
|
||||
CYRF_WriteRegister(CYRF_25_MFG_ID, 0x00);
|
||||
#else
|
||||
memcpy(data,FORCE_CYRF_ID,6);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -128,17 +98,25 @@ void CYRF_SetTxRxMode(uint8_t mode)
|
||||
{
|
||||
if(mode==TXRX_OFF)
|
||||
{
|
||||
if( protocol!=PROTO_WFLY && protocol!=PROTO_MLINK )
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24); // 4=IDLE, 8=TX, C=RX
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x00); // XOUT=0 PACTL=0
|
||||
}
|
||||
else
|
||||
{
|
||||
//Set the post tx/rx state
|
||||
if( protocol!=PROTO_WFLY && protocol!=PROTO_MLINK )
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, mode == TX_EN ? 0x28 : 0x2C); // 4=IDLE, 8=TX, C=RX
|
||||
if(mode == TX_EN)
|
||||
#ifdef ORANGE_TX_BLUE
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x20); // XOUT=1, PACTL=0
|
||||
else
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x80); // XOUT=0, PACTL=1
|
||||
#else
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x80); // XOUT=1, PACTL=0
|
||||
else
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL,0x20); // XOUT=0, PACTL=1
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -149,29 +127,51 @@ void CYRF_ConfigRFChannel(uint8_t ch)
|
||||
CYRF_WriteRegister(CYRF_00_CHANNEL,ch);
|
||||
}
|
||||
|
||||
void CYRF_SetPower_Value(uint8_t power)
|
||||
/*
|
||||
static void CYRF_SetPower_Value(uint8_t power)
|
||||
{
|
||||
uint8_t val = CYRF_ReadRegister(CYRF_03_TX_CFG) & 0xF8;
|
||||
CYRF_WriteRegister(CYRF_03_TX_CFG, val | (power & 0x07));
|
||||
}
|
||||
*/
|
||||
|
||||
void CYRF_SetPower(uint8_t val)
|
||||
{
|
||||
uint8_t power=CYRF_BIND_POWER;
|
||||
if(IS_BIND_DONE_on)
|
||||
if(IS_BIND_DONE)
|
||||
#ifdef CYRF6936_ENABLE_LOW_POWER
|
||||
power=IS_POWER_FLAG_on?CYRF_HIGH_POWER:CYRF_LOW_POWER;
|
||||
#else
|
||||
power=CYRF_HIGH_POWER;
|
||||
#endif
|
||||
if(IS_RANGE_FLAG_on)
|
||||
power=CYRF_RANGE_POWER;
|
||||
CYRF_WriteRegister(CYRF_03_TX_CFG, val | power);
|
||||
power|=val;
|
||||
if(prev_power != power)
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_03_TX_CFG,power);
|
||||
prev_power=power;
|
||||
}
|
||||
|
||||
#ifdef USE_CYRF6936_CH15_TUNING
|
||||
static uint16_t Channel15=1024;
|
||||
if(Channel15!=Channel_data[CH15])
|
||||
{ // adjust frequency
|
||||
Channel15=Channel_data[CH15]+0x155; // default value is 0x555 = 0x400 + 0x155
|
||||
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, Channel15&0xFF);
|
||||
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, Channel15>>8);
|
||||
Channel15-=0x155;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CYRF_ConfigCRCSeed(uint16_t crc)
|
||||
void CYRF_ConfigCRCSeed(uint16_t crc_seed)
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_15_CRC_SEED_LSB,crc & 0xff);
|
||||
CYRF_WriteRegister(CYRF_16_CRC_SEED_MSB,crc >> 8);
|
||||
CYRF_WriteRegister(CYRF_15_CRC_SEED_LSB,crc_seed & 0xff);
|
||||
CYRF_WriteRegister(CYRF_16_CRC_SEED_MSB,crc_seed >> 8);
|
||||
}
|
||||
/*
|
||||
* these are the recommended sop codes from Cyrpress
|
||||
@@ -194,36 +194,31 @@ void CYRF_ConfigDataCode(const uint8_t *datacodes, uint8_t len)
|
||||
void CYRF_WritePreamble(uint32_t preamble)
|
||||
{
|
||||
CYRF_CSN_off;
|
||||
cyrf_spi_write(0x80 | 0x24);
|
||||
cyrf_spi_write(preamble & 0xff);
|
||||
cyrf_spi_write((preamble >> 8) & 0xff);
|
||||
cyrf_spi_write((preamble >> 16) & 0xff);
|
||||
SPI_Write(0x80 | 0x24);
|
||||
SPI_Write(preamble & 0xff);
|
||||
SPI_Write((preamble >> 8) & 0xff);
|
||||
SPI_Write((preamble >> 16) & 0xff);
|
||||
CYRF_CSN_on;
|
||||
}
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void CYRF_StartReceive()
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL,0x87);
|
||||
}
|
||||
|
||||
void CYRF_ReadDataPacket(uint8_t dpbuffer[])
|
||||
/*static void CYRF_ReadDataPacket(uint8_t dpbuffer[])
|
||||
{
|
||||
CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, 0x10);
|
||||
}
|
||||
|
||||
*/
|
||||
void CYRF_ReadDataPacketLen(uint8_t dpbuffer[], uint8_t length)
|
||||
{
|
||||
ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, length);
|
||||
CYRF_ReadRegisterMulti(CYRF_21_RX_BUFFER, dpbuffer, length);
|
||||
}
|
||||
|
||||
void CYRF_WriteDataPacketLen(const uint8_t dpbuffer[], uint8_t len)
|
||||
static void CYRF_WriteDataPacketLen(const uint8_t dpbuffer[], uint8_t len)
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_01_TX_LENGTH, len);
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40);
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x43); // 0x40
|
||||
CYRF_WriteRegisterMulti(CYRF_20_TX_BUFFER, dpbuffer, len);
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0xBF);
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x83); // 0xBF
|
||||
}
|
||||
|
||||
void CYRF_WriteDataPacket(const uint8_t dpbuffer[])
|
||||
@@ -231,7 +226,7 @@ void CYRF_WriteDataPacket(const uint8_t dpbuffer[])
|
||||
CYRF_WriteDataPacketLen(dpbuffer, 16);
|
||||
}
|
||||
|
||||
uint8_t CYRF_ReadRSSI(uint8_t dodummyread)
|
||||
/*static uint8_t CYRF_ReadRSSI(uint8_t dodummyread)
|
||||
{
|
||||
uint8_t result;
|
||||
if(dodummyread)
|
||||
@@ -241,7 +236,7 @@ uint8_t CYRF_ReadRSSI(uint8_t dodummyread)
|
||||
result = CYRF_ReadRegister(CYRF_13_RSSI);
|
||||
return (result & 0x0F);
|
||||
}
|
||||
|
||||
*/
|
||||
//NOTE: This routine will reset the CRC Seed
|
||||
void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uint8_t min, uint8_t max)
|
||||
{
|
||||
@@ -260,14 +255,18 @@ void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uin
|
||||
CYRF_ConfigCRCSeed(0x0000);
|
||||
CYRF_SetTxRxMode(RX_EN);
|
||||
//Wait for pre-amp to switch from send to receive
|
||||
_delay_us(1000);
|
||||
delayMilliseconds(1);
|
||||
for(i = 0; i < NUM_FREQ; i++)
|
||||
{
|
||||
CYRF_ConfigRFChannel(i);
|
||||
CYRF_ReadRegister(CYRF_13_RSSI);
|
||||
CYRF_StartReceive();
|
||||
_delay_us(10);
|
||||
rssi[i] = CYRF_ReadRegister(CYRF_13_RSSI);
|
||||
delayMicroseconds(270); //slow channel require 270usec for synthesizer to settle
|
||||
if( !(CYRF_ReadRegister(CYRF_05_RX_CTRL) & 0x80)) {
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80); //Prepare to receive
|
||||
delayMicroseconds(15);
|
||||
CYRF_ReadRegister(CYRF_13_RSSI); //dummy read
|
||||
delayMicroseconds(15); //The conversion can occur as often as once every 12us
|
||||
}
|
||||
rssi[i] = CYRF_ReadRegister(CYRF_13_RSSI)&0x1F;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
@@ -283,5 +282,108 @@ void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uin
|
||||
rssi[j] = 0xff;
|
||||
}
|
||||
}
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX
|
||||
}
|
||||
|
||||
#if defined(DEVO_CYRF6936_INO) || defined(J6PRO_CYRF6936_INO)
|
||||
const uint8_t PROGMEM DEVO_j6pro_sopcodes[][8] = {
|
||||
/* Note these are in order transmitted (LSB 1st) */
|
||||
{0x3C, 0x37, 0xCC, 0x91, 0xE2, 0xF8, 0xCC, 0x91},
|
||||
{0x9B, 0xC5, 0xA1, 0x0F, 0xAD, 0x39, 0xA2, 0x0F},
|
||||
{0xEF, 0x64, 0xB0, 0x2A, 0xD2, 0x8F, 0xB1, 0x2A},
|
||||
{0x66, 0xCD, 0x7C, 0x50, 0xDD, 0x26, 0x7C, 0x50},
|
||||
{0x5C, 0xE1, 0xF6, 0x44, 0xAD, 0x16, 0xF6, 0x44},
|
||||
{0x5A, 0xCC, 0xAE, 0x46, 0xB6, 0x31, 0xAE, 0x46},
|
||||
{0xA1, 0x78, 0xDC, 0x3C, 0x9E, 0x82, 0xDC, 0x3C},
|
||||
{0xB9, 0x8E, 0x19, 0x74, 0x6F, 0x65, 0x18, 0x74},
|
||||
{0xDF, 0xB1, 0xC0, 0x49, 0x62, 0xDF, 0xC1, 0x49},
|
||||
{0x97, 0xE5, 0x14, 0x72, 0x7F, 0x1A, 0x14, 0x72},
|
||||
#if defined(J6PRO_CYRF6936_INO)
|
||||
{0x82, 0xC7, 0x90, 0x36, 0x21, 0x03, 0xFF, 0x17},
|
||||
{0xE2, 0xF8, 0xCC, 0x91, 0x3C, 0x37, 0xCC, 0x91}, //Note: the '03' was '9E' in the Cypress recommended table
|
||||
{0xAD, 0x39, 0xA2, 0x0F, 0x9B, 0xC5, 0xA1, 0x0F}, //The following are the same as the 1st 8 above,
|
||||
{0xD2, 0x8F, 0xB1, 0x2A, 0xEF, 0x64, 0xB0, 0x2A}, //but with the upper and lower word swapped
|
||||
{0xDD, 0x26, 0x7C, 0x50, 0x66, 0xCD, 0x7C, 0x50},
|
||||
{0xAD, 0x16, 0xF6, 0x44, 0x5C, 0xE1, 0xF6, 0x44},
|
||||
{0xB6, 0x31, 0xAE, 0x46, 0x5A, 0xCC, 0xAE, 0x46},
|
||||
{0x9E, 0x82, 0xDC, 0x3C, 0xA1, 0x78, 0xDC, 0x3C},
|
||||
{0x6F, 0x65, 0x18, 0x74, 0xB9, 0x8E, 0x19, 0x74},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) CYRF_PROGMEM_ConfigSOPCode(const uint8_t *data)
|
||||
{
|
||||
uint8_t code[8];
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
code[i]=pgm_read_byte_near(&data[i]);
|
||||
CYRF_ConfigSOPCode(code);
|
||||
}
|
||||
|
||||
//CYRF GFSK 1Mb functions
|
||||
const uint8_t PROGMEM CYRF_GFSK1M_init_vals[][2] = {
|
||||
{CYRF_02_TX_CTRL, 0x00}, // transmit err & complete interrupts disabled
|
||||
{CYRF_05_RX_CTRL, 0x00}, // receive err & complete interrupts disabled
|
||||
{CYRF_28_CLK_EN, 0x02}, // Force Receive Clock Enable, MUST be set
|
||||
{CYRF_32_AUTO_CAL_TIME, 0x3c}, // must be set to 3C
|
||||
{CYRF_35_AUTOCAL_OFFSET, 0x14}, // must be set to 14
|
||||
{CYRF_06_RX_CFG, 0x48}, // LNA manual control, Rx Fast Turn Mode Enable
|
||||
{CYRF_1B_TX_OFFSET_LSB, 0x00}, // Tx frequency offset LSB
|
||||
{CYRF_1C_TX_OFFSET_MSB, 0x00}, // Tx frequency offset MSB
|
||||
{CYRF_0F_XACT_CFG, 0x24}, // Force End State, transaction end state = idle
|
||||
{CYRF_03_TX_CFG, 0x00}, // GFSK mode
|
||||
{CYRF_12_DATA64_THOLD, 0x0a}, // 64 Chip Data PN Code Correlator Threshold = 10
|
||||
{CYRF_0F_XACT_CFG, 0x04}, // Transaction End State = idle
|
||||
{CYRF_39_ANALOG_CTRL, 0x01}, // synth setting time for all channels is the same as for slow channels
|
||||
{CYRF_0F_XACT_CFG, 0x24}, //Force IDLE
|
||||
{CYRF_29_RX_ABORT, 0x00}, //Clear RX abort
|
||||
{CYRF_12_DATA64_THOLD, 0x0a}, //set pn correlation threshold
|
||||
{CYRF_10_FRAMING_CFG, 0x4a}, //set sop len and threshold
|
||||
{CYRF_29_RX_ABORT, 0x0f}, //Clear RX abort?
|
||||
{CYRF_03_TX_CFG, 0x00}, // GFSK mode
|
||||
{CYRF_10_FRAMING_CFG, 0x4a}, // 0b11000000 //set sop len and threshold
|
||||
{CYRF_1F_TX_OVERRIDE, 0x04}, //disable tx CRC
|
||||
{CYRF_1E_RX_OVERRIDE, 0x14}, //disable rx crc
|
||||
{CYRF_14_EOP_CTRL, 0x00}, //set EOP sync == 0
|
||||
};
|
||||
static void __attribute__((unused)) CYRF_GFSK1M_Init(uint8_t payload_length, uint8_t preamble_len)
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(CYRF_GFSK1M_init_vals) / 2; i++)
|
||||
CYRF_WriteRegister(pgm_read_byte_near(&CYRF_GFSK1M_init_vals[i][0]), pgm_read_byte_near(&CYRF_GFSK1M_init_vals[i][1]));
|
||||
|
||||
|
||||
CYRF_WriteRegister(CYRF_01_TX_LENGTH, payload_length);
|
||||
|
||||
CYRF_WritePreamble(0xAAAA00 | preamble_len);
|
||||
|
||||
CYRF_SetPower(0x00);
|
||||
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
}
|
||||
static void __attribute__((unused)) CYRF_GFSK1M_SendPayload(uint8_t *buffer, uint8_t len)
|
||||
{
|
||||
uint8_t send=len>16 ? 16 : len;
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40);
|
||||
CYRF_WriteRegisterMulti(CYRF_20_TX_BUFFER, buffer, send); // Fill the buffer with 16 bytes max
|
||||
CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x80); // Start send
|
||||
buffer += send;
|
||||
len -= send;
|
||||
|
||||
while(len>8)
|
||||
{
|
||||
while((CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS)&0x10) == 0); // Wait that half of the buffer is empty
|
||||
CYRF_WriteRegisterMulti(CYRF_20_TX_BUFFER, buffer, 8); // Add 8 bytes to the buffer
|
||||
buffer+=8;
|
||||
len-=8;
|
||||
}
|
||||
|
||||
if(len)
|
||||
{
|
||||
while((CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS)&0x10) == 0); // Wait that half of the buffer is empty
|
||||
CYRF_WriteRegisterMulti(CYRF_20_TX_BUFFER, buffer, len); // Add the remaining bytes to the buffer
|
||||
}
|
||||
}
|
||||
#define CYRF_GFSK1M_SetPower() CYRF_SetPower(0x00)
|
||||
#endif
|
||||
176
Multiprotocol/Convert.ino
Normal file
176
Multiprotocol/Convert.ino
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/************************/
|
||||
/** Convert routines **/
|
||||
/************************/
|
||||
// Reverse a channel and store it
|
||||
void reverse_channel(uint8_t num)
|
||||
{
|
||||
uint16_t val=2048-Channel_data[num];
|
||||
if(val>=2048) val=2047;
|
||||
Channel_data[num]=val;
|
||||
}
|
||||
|
||||
// Channel value is converted to ppm 860<->2140 -125%<->+125% and 988<->2012 -100%<->+100%
|
||||
uint16_t convert_channel_ppm(uint8_t num)
|
||||
{
|
||||
uint16_t val=Channel_data[num];
|
||||
return (((val<<2)+val)>>3)+860; //value range 860<->2140 -125%<->+125%
|
||||
}
|
||||
|
||||
// Channel value 100% is converted to 10bit values 0<->1023
|
||||
uint16_t convert_channel_10b(uint8_t num, bool failsafe)
|
||||
{
|
||||
uint16_t val;
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(failsafe)
|
||||
val=Failsafe_data[num]; // 0<->2047
|
||||
else
|
||||
#endif
|
||||
val=Channel_data[num];
|
||||
val=((val<<2)+val)>>3;
|
||||
if(val<=128) return 0;
|
||||
if(val>=1152) return 1023;
|
||||
return val-128;
|
||||
}
|
||||
|
||||
// Channel value 100% is converted to 8bit values 0<->255
|
||||
uint8_t convert_channel_8b(uint8_t num)
|
||||
{
|
||||
uint16_t val=Channel_data[num];
|
||||
val=((val<<2)+val)>>5;
|
||||
if(val<=32) return 0;
|
||||
if(val>=288) return 255;
|
||||
return val-32;
|
||||
}
|
||||
|
||||
// Channel value 100% is converted to 8b with deadband
|
||||
uint8_t convert_channel_8b_limit_deadband(uint8_t num,uint8_t min,uint8_t mid, uint8_t max, uint8_t deadband)
|
||||
{
|
||||
uint16_t val=limit_channel_100(num); // 204<->1844
|
||||
uint16_t db_low=CHANNEL_MID-deadband, db_high=CHANNEL_MID+deadband; // 1024+-deadband
|
||||
int32_t calc;
|
||||
uint8_t out;
|
||||
if(val>=db_low && val<=db_high)
|
||||
return mid;
|
||||
else if(val<db_low)
|
||||
{
|
||||
val-=CHANNEL_MIN_100;
|
||||
calc=mid-min;
|
||||
calc*=val;
|
||||
calc/=(db_low-CHANNEL_MIN_100);
|
||||
out=calc;
|
||||
out+=min;
|
||||
}
|
||||
else
|
||||
{
|
||||
val-=db_high;
|
||||
calc=max-mid;
|
||||
calc*=val;
|
||||
calc/=(CHANNEL_MAX_100-db_high+1);
|
||||
out=calc;
|
||||
out+=mid;
|
||||
if(max>min) out++; else out--;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Channel value 100% is converted to value scaled
|
||||
int16_t convert_channel_16b_limit(uint8_t num,int16_t min,int16_t max)
|
||||
{
|
||||
int32_t val=limit_channel_100(num); // 204<->1844
|
||||
val=(val-CHANNEL_MIN_100)*(max-min)/(CHANNEL_MAX_100-CHANNEL_MIN_100)+min;
|
||||
return (uint16_t)val;
|
||||
}
|
||||
|
||||
// Channel value -125%<->125% is scaled to 16bit value with no limit
|
||||
int16_t convert_channel_16b_nolimit(uint8_t num, int16_t min, int16_t max, bool failsafe)
|
||||
{
|
||||
int32_t val;
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(failsafe)
|
||||
val=Failsafe_data[num]; // 0<->2047
|
||||
else
|
||||
#endif
|
||||
val=Channel_data[num]; // 0<->2047
|
||||
val=(val-CHANNEL_MIN_100)*(max-min)/(CHANNEL_MAX_100-CHANNEL_MIN_100)+min;
|
||||
return (uint16_t)val;
|
||||
}
|
||||
|
||||
// Channel value is converted sign + magnitude 8bit values
|
||||
uint8_t convert_channel_s8b(uint8_t num)
|
||||
{
|
||||
uint8_t ch;
|
||||
ch = convert_channel_8b(num);
|
||||
return (ch < 128 ? 127-ch : ch);
|
||||
}
|
||||
|
||||
// Channel value is limited to 100%
|
||||
uint16_t limit_channel_100(uint8_t num)
|
||||
{
|
||||
if(Channel_data[num]>=CHANNEL_MAX_100)
|
||||
return CHANNEL_MAX_100;
|
||||
if (Channel_data[num]<=CHANNEL_MIN_100)
|
||||
return CHANNEL_MIN_100;
|
||||
return Channel_data[num];
|
||||
}
|
||||
|
||||
// Channel value is converted for HK310
|
||||
void convert_channel_HK310(uint8_t num, uint8_t *low, uint8_t *high)
|
||||
{
|
||||
uint16_t temp=0xFFFF-(3440+((Channel_data[num]*5)>>1))/3;
|
||||
*low=(uint8_t)(temp&0xFF);
|
||||
*high=(uint8_t)(temp>>8);
|
||||
}
|
||||
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
// Failsafe value is converted for HK310
|
||||
void convert_failsafe_HK310(uint8_t num, uint8_t *low, uint8_t *high)
|
||||
{
|
||||
uint16_t temp=0xFFFF-(3440+((Failsafe_data[num]*5)>>1))/3;
|
||||
*low=(uint8_t)(temp&0xFF);
|
||||
*high=(uint8_t)(temp>>8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Channel value for FrSky (PPM is multiplied by 1.5)
|
||||
uint16_t convert_channel_frsky(uint8_t num)
|
||||
{
|
||||
uint16_t val=Channel_data[num];
|
||||
return ((val*15)>>4)+1290;
|
||||
}
|
||||
|
||||
// 0-2047, 0 = 817, 1024 = 1500, 2047 = 2182
|
||||
//64=860,1024=1500,1984=2140//Taranis 125%
|
||||
static uint16_t __attribute__((unused)) FrSkyX_scaleForPXX( uint8_t i, uint8_t num_chan=8)
|
||||
{ //mapped 860,2140(125%) range to 64,1984(PXX values);
|
||||
uint16_t chan_val=convert_channel_frsky(i)-1226;
|
||||
if(i>=num_chan) chan_val|=2048; // upper channels offset
|
||||
return chan_val;
|
||||
}
|
||||
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
static uint16_t __attribute__((unused)) FrSkyX_scaleForPXX_FS( uint8_t i, uint8_t num_chan=8)
|
||||
{ //mapped 1,2046(125%) range to 64,1984(PXX values);
|
||||
uint16_t chan_val=((Failsafe_data[i]*15)>>4)+64;
|
||||
if(Failsafe_data[i]==FAILSAFE_CHANNEL_NOPULSES)
|
||||
chan_val=FAILSAFE_CHANNEL_NOPULSES;
|
||||
else if(Failsafe_data[i]==FAILSAFE_CHANNEL_HOLD)
|
||||
chan_val=FAILSAFE_CHANNEL_HOLD;
|
||||
if(i>=num_chan) chan_val|=2048; // upper channels offset
|
||||
return chan_val;
|
||||
}
|
||||
#endif
|
||||
300
Multiprotocol/Corona_cc2500.ino
Normal file
300
Multiprotocol/Corona_cc2500.ino
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(CORONA_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define CORONA_FORCE_ID
|
||||
|
||||
#define CORONA_RF_NUM_CHANNELS 3
|
||||
#define CORONA_ADDRESS_LENGTH 4
|
||||
#define CORONA_BIND_CHANNEL_V1 0xD1 // also Flydream V3
|
||||
#define CORONA_BIND_CHANNEL_V2 0xB8
|
||||
#define CORONA_COARSE 0x00
|
||||
#define FDV3_BIND_PERIOD 5000
|
||||
#define FDV3_CHANNEL_PERIOD 4000
|
||||
|
||||
const PROGMEM uint8_t CORONA_init_values[] = {
|
||||
/* 00 */ 0x29, 0x2E, 0x06, 0x07, 0xD3, 0x91, 0xFF, 0x04,
|
||||
/* 08 */ 0x05, 0x00, CORONA_BIND_CHANNEL_V1, 0x06, 0x00, 0x5C, 0x4E, 0xC4 + CORONA_COARSE,
|
||||
/* 10 */ 0x5B, 0xF8, 0x03, 0x23, 0xF8, 0x47, 0x07, 0x30,
|
||||
/* 18 */ 0x18, 0x16, 0x6C, 0x43, 0x40, 0x91, 0x87, 0x6B,
|
||||
/* 20 */ 0xF8, 0x56, 0x10, 0xA9, 0x0A, 0x00, 0x11, 0x41,
|
||||
/* 28 */ 0x00, 0x59, 0x7F, 0x3F, 0x81, 0x35, 0x0B
|
||||
};
|
||||
|
||||
uint8_t fdv3_id_send;
|
||||
|
||||
static void __attribute__((unused)) CORONA_rf_init()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
for (uint8_t i = 0; i <= 0x2E; ++i)
|
||||
CC2500_WriteReg(i, pgm_read_byte_near(&CORONA_init_values[i]));
|
||||
if(sub_protocol==COR_V2)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, CORONA_BIND_CHANNEL_V2);
|
||||
CC2500_WriteReg(CC2500_0E_FREQ1, 0x80);
|
||||
CC2500_WriteReg(CC2500_0F_FREQ0, 0x00 + CORONA_COARSE);
|
||||
CC2500_WriteReg(CC2500_15_DEVIATN, 0x50);
|
||||
CC2500_WriteReg(CC2500_17_MCSM1, 0x00);
|
||||
CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0x67);
|
||||
CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0xFB);
|
||||
CC2500_WriteReg(CC2500_1D_AGCCTRL0, 0xDC);
|
||||
}
|
||||
else if(sub_protocol==FD_V3)
|
||||
{
|
||||
// Flydream receiver captures have deviation 50, tx captures show 47
|
||||
CC2500_WriteReg(CC2500_15_DEVIATN, 0x50);
|
||||
}
|
||||
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
|
||||
//not sure what they are doing to the PATABLE since basically only the first byte is used and it's only 8 bytes long. So I think they end up filling the PATABLE fully with 0xFF
|
||||
CC2500_WriteRegisterMulti(CC2500_3E_PATABLE,(const uint8_t *)"\x08\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 13);
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
}
|
||||
|
||||
// Generate id and hopping freq
|
||||
static void __attribute__((unused)) CORONA_TXID_init()
|
||||
{
|
||||
#ifdef CORONA_FORCE_ID
|
||||
// Example of ID and channels taken from dumps
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case COR_V1:
|
||||
memcpy((void *)rx_tx_addr,(void *)"\x1F\xFE\x6C\x35",CORONA_ADDRESS_LENGTH);
|
||||
memcpy((void *)hopping_frequency,(void *)"\x17\x0D\x03\x49",CORONA_RF_NUM_CHANNELS+1);
|
||||
break;
|
||||
case COR_V2:
|
||||
memcpy((void *)rx_tx_addr,(void *)"\xFE\xFE\x02\xFB",CORONA_ADDRESS_LENGTH);
|
||||
memcpy((void *)hopping_frequency,(void *)"\x14\x3D\x35",CORONA_RF_NUM_CHANNELS);
|
||||
case FD_V3:
|
||||
memcpy((void *)rx_tx_addr,(void *)"\x02\xFA\x38\x38",CORONA_ADDRESS_LENGTH);
|
||||
memcpy((void *)hopping_frequency,(void *)"\x71\xB9\x30",CORONA_RF_NUM_CHANNELS);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
// From dumps channels are anything between 0x00 and 0xC5 on V1.
|
||||
// But 0x00 and 0xB8 should be avoided on V2 since they are used for bind.
|
||||
// Below code make sure channels are between 0x02 and 0xA0, spaced with
|
||||
// a minimum of 2 and not ordered (RX only use the 1st channel unless there is an issue).
|
||||
// Extra hopping frequency used for Flydream V3 id packets.
|
||||
uint8_t order=rx_tx_addr[3]&0x03;
|
||||
for(uint8_t i=0; i<CORONA_RF_NUM_CHANNELS+1; i++)
|
||||
hopping_frequency[i^order]=2+rx_tx_addr[3-i]%39+(i<<5)+(i<<3);
|
||||
|
||||
if(sub_protocol!=FD_V3)
|
||||
{
|
||||
// ID looks random but on the 15 V1 dumps they all show the same odd/even rule
|
||||
if(rx_tx_addr[3]&0x01)
|
||||
{ // If [3] is odd then [0] is odd and [2] is even
|
||||
rx_tx_addr[0]|=0x01;
|
||||
rx_tx_addr[2]&=0xFE;
|
||||
}
|
||||
else
|
||||
{ // If [3] is even then [0] is even and [2] is odd
|
||||
rx_tx_addr[0]&=0xFE;
|
||||
rx_tx_addr[2]|=0x01;
|
||||
}
|
||||
rx_tx_addr[1]=0xFE; // Always FE in the dumps of V1 and V2
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_tx_addr[1]=0xFA; // Always FA for Flydream V3
|
||||
rx_tx_addr[3]=hopping_frequency[CORONA_RF_NUM_CHANNELS]; // channel used for id/freq packets
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint16_t __attribute__((unused)) CORONA_build_bind_pkt()
|
||||
{
|
||||
if(sub_protocol==COR_V1)
|
||||
{ // V1
|
||||
if(bind_counter&1)
|
||||
{ // Send TX ID
|
||||
packet[0]=0x04; // 5 bytes to follow
|
||||
for(uint8_t i=0; i<CORONA_ADDRESS_LENGTH; i++)
|
||||
packet[i+1]=rx_tx_addr[i];
|
||||
packet[5]=0xCD; // Unknown but seems to be always the same value for V1
|
||||
return 3689;
|
||||
}
|
||||
else
|
||||
{ // Send hopping freq
|
||||
packet[0]=0x03; // 4 bytes to follow
|
||||
for(uint8_t i=0; i<CORONA_RF_NUM_CHANNELS+1; i++)
|
||||
packet[i+1]=hopping_frequency[i];
|
||||
// Only the first 3 channels of hopping_frequency used for data
|
||||
return 3438;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // V2 and FDV3
|
||||
packet[0]=0x04; // 5 bytes to follow
|
||||
for(uint8_t i=0; i<CORONA_ADDRESS_LENGTH; i++)
|
||||
packet[i+1]=rx_tx_addr[i];
|
||||
packet[5]=0x00; // Unknown but seems to be always the same value for V2 and FDV3
|
||||
if(sub_protocol==FD_V3)
|
||||
return FDV3_BIND_PERIOD;
|
||||
else
|
||||
return 26791;
|
||||
}
|
||||
}
|
||||
|
||||
// 8 Channels with direct values from PPM
|
||||
static uint16_t __attribute__((unused)) CORONA_build_packet()
|
||||
{
|
||||
CC2500_SetPower();
|
||||
if(state && sub_protocol==COR_V2)
|
||||
{ // Send identifier packet for 2.65sec. This is how the RX learns the hopping table after a bind. Why it's not part of the bind like V1 is a mistery...
|
||||
// Set channel
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
|
||||
state--;
|
||||
packet[0]=0x07; // 8 bytes to follow
|
||||
// Send hopping freq
|
||||
for(uint8_t i=0; i<CORONA_RF_NUM_CHANNELS; i++)
|
||||
packet[i+1]=hopping_frequency[i];
|
||||
// Send TX ID
|
||||
for(uint8_t i=0; i<CORONA_ADDRESS_LENGTH; i++)
|
||||
packet[i+4]=rx_tx_addr[i];
|
||||
packet[8]=0;
|
||||
return 6647;
|
||||
}
|
||||
|
||||
// Flydream every fourth packet is identifier packet and is on channel number
|
||||
// that is last byte of rx_tx_addr
|
||||
if (fdv3_id_send)
|
||||
{
|
||||
fdv3_id_send = 0;
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, rx_tx_addr[CORONA_ADDRESS_LENGTH-1]);
|
||||
packet[0] = 0x07; // 8 bytes to follow
|
||||
// Send TX ID
|
||||
for(uint8_t i = 0; i < CORONA_ADDRESS_LENGTH; i++)
|
||||
packet[i+1] = rx_tx_addr[i];
|
||||
// Send hopping freq
|
||||
for(uint8_t i = 0; i < CORONA_RF_NUM_CHANNELS; i++)
|
||||
packet[i+1+CORONA_ADDRESS_LENGTH] = hopping_frequency[i];
|
||||
packet[8] = 0;
|
||||
return 2*FDV3_CHANNEL_PERIOD; // extra delay after id packet according to captures
|
||||
}
|
||||
|
||||
// Set RF channel
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[hopping_frequency_no]);
|
||||
|
||||
// Build packet
|
||||
packet[0] = 0x10; // 17 bytes to follow
|
||||
|
||||
// Channels
|
||||
memset(packet+9, 0x00, 4);
|
||||
for (uint8_t i=0; i<8; i++)
|
||||
{ // Channel values are packed
|
||||
uint16_t val=convert_channel_ppm(i);
|
||||
packet[i+1] = val;
|
||||
packet[9 + (i>>1)] |= (i&0x01)?(val>>4)&0xF0:(val>>8)&0x0F;
|
||||
}
|
||||
|
||||
// TX ID
|
||||
for (uint8_t i=0; i < CORONA_ADDRESS_LENGTH; i++)
|
||||
packet[i+13] = rx_tx_addr[i];
|
||||
|
||||
packet[17] = 0x00;
|
||||
|
||||
if (sub_protocol!=FD_V3)
|
||||
{
|
||||
// Packet period is based on hopping
|
||||
switch (hopping_frequency_no)
|
||||
{
|
||||
case 0:
|
||||
packet_period = sub_protocol == COR_V1
|
||||
? 4991
|
||||
: 4248;
|
||||
break;
|
||||
case 1:
|
||||
packet_period = sub_protocol == COR_V1
|
||||
? 4991
|
||||
: 4345;
|
||||
break;
|
||||
case 2:
|
||||
packet_period = sub_protocol == COR_V1
|
||||
? 12520
|
||||
: 13468;
|
||||
if (sub_protocol == COR_V2)
|
||||
packet[17] = 0x03;
|
||||
break;
|
||||
}
|
||||
}
|
||||
hopping_frequency_no++;
|
||||
|
||||
if (sub_protocol == FD_V3)
|
||||
{
|
||||
if (hopping_frequency_no == CORONA_RF_NUM_CHANNELS)
|
||||
{
|
||||
fdv3_id_send = 1;
|
||||
packet_period = 6000; // extra delay before id packet according to captures
|
||||
}
|
||||
else
|
||||
packet_period = FDV3_CHANNEL_PERIOD;
|
||||
}
|
||||
|
||||
hopping_frequency_no %= CORONA_RF_NUM_CHANNELS;
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
uint16_t CORONA_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(22000);
|
||||
#endif
|
||||
// Tune frequency if it has been changed
|
||||
CC2500_SetFreqOffset();
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
if (bind_counter-- == 0) BIND_DONE;
|
||||
packet_period=CORONA_build_bind_pkt();
|
||||
}
|
||||
else
|
||||
packet_period=CORONA_build_packet();
|
||||
|
||||
// Send packet
|
||||
CC2500_WriteData(packet, packet[0]+2);
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void CORONA_init()
|
||||
{
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case COR_V1:
|
||||
bind_counter=1400; // Stay in bind mode for 5s
|
||||
break;
|
||||
case COR_V2:
|
||||
bind_counter=187; // Stay in bind mode for 5s
|
||||
break;
|
||||
case FD_V3:
|
||||
bind_counter = 2000; // Stay in bind mode for 10s
|
||||
break;
|
||||
}
|
||||
state=400; // Used by V2 to send RF channels + ID for 2.65s at startup
|
||||
hopping_frequency_no=0;
|
||||
fdv3_id_send = 0;
|
||||
CORONA_TXID_init();
|
||||
CORONA_rf_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
148
Multiprotocol/DM002_nrf24l01.ino
Normal file
148
Multiprotocol/DM002_nrf24l01.ino
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with DM002
|
||||
|
||||
#if defined(DM002_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define DM002_PACKET_PERIOD 6100 // Timeout for callback in uSec
|
||||
#define DM002_INITIAL_WAIT 500
|
||||
#define DM002_PACKET_SIZE 12 // packets have 12-byte payload
|
||||
#define DM002_RF_BIND_CHANNEL 0x27
|
||||
#define DM002_BIND_COUNT 655 // 4 seconds
|
||||
|
||||
|
||||
enum DM002_FLAGS {
|
||||
// flags going to packet[9]
|
||||
DM002_FLAG_FLIP = 0x01,
|
||||
DM002_FLAG_LED = 0x02,
|
||||
DM002_FLAG_MEDIUM = 0x04,
|
||||
DM002_FLAG_HIGH = 0x08,
|
||||
DM002_FLAG_RTH = 0x10,
|
||||
DM002_FLAG_HEADLESS = 0x20,
|
||||
DM002_FLAG_CAMERA1 = 0x40,
|
||||
DM002_FLAG_CAMERA2 = 0x80,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DM002_send_packet()
|
||||
{
|
||||
memcpy(packet+5,(uint8_t *)"\x00\x7F\x7F\x7F\x00\x00\x00",7);
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0xAA;
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[0]=0x55;
|
||||
// Throttle : 0 .. 200
|
||||
packet[1]=convert_channel_16b_limit(THROTTLE,0,200);
|
||||
// Other channels min 0x57, mid 0x7F, max 0xA7
|
||||
packet[2] = convert_channel_16b_limit(RUDDER,0x57,0xA7);
|
||||
packet[3] = convert_channel_16b_limit(AILERON, 0x57,0xA7);
|
||||
packet[4] = convert_channel_16b_limit(ELEVATOR, 0xA7, 0x57);
|
||||
// Features
|
||||
packet[9] = GET_FLAG(CH5_SW,DM002_FLAG_FLIP)
|
||||
| GET_FLAG(!CH6_SW,DM002_FLAG_LED)
|
||||
| GET_FLAG(CH7_SW,DM002_FLAG_CAMERA1)
|
||||
| GET_FLAG(CH8_SW,DM002_FLAG_CAMERA2)
|
||||
| GET_FLAG(CH9_SW,DM002_FLAG_HEADLESS)
|
||||
| GET_FLAG(CH10_SW,DM002_FLAG_RTH)
|
||||
| GET_FLAG(!CH11_SW,DM002_FLAG_HIGH);
|
||||
// Packet counter
|
||||
if(packet_count&0x03)
|
||||
{
|
||||
packet_count++;
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no&=4;
|
||||
}
|
||||
packet_count&=0x0F;
|
||||
packet[10] = packet_count;
|
||||
packet_count++;
|
||||
}
|
||||
//CRC
|
||||
for(uint8_t i=0;i<DM002_PACKET_SIZE-1;i++)
|
||||
packet[11]+=packet[i];
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? DM002_RF_BIND_CHANNEL : hopping_frequency[hopping_frequency_no]);
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, DM002_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DM002_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
|
||||
}
|
||||
|
||||
uint16_t DM002_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(DM002_PACKET_PERIOD);
|
||||
#endif
|
||||
if (bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
XN297_SetTXAddr(rx_tx_addr, 5);
|
||||
}
|
||||
}
|
||||
DM002_send_packet();
|
||||
return DM002_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DM002_initialize_txid()
|
||||
{
|
||||
// Only 3 IDs/RFs are available, RX_NUM is used to switch between them
|
||||
switch(rx_tx_addr[3]%3)
|
||||
{
|
||||
case 0:
|
||||
memcpy(hopping_frequency,(uint8_t *)"\x34\x39\x43\x48",4);
|
||||
memcpy(rx_tx_addr,(uint8_t *)"\x47\x93\x00\x00\xD5",5);
|
||||
break;
|
||||
case 1:
|
||||
memcpy(hopping_frequency,(uint8_t *)"\x35\x39\x3B\x3D",4);
|
||||
memcpy(rx_tx_addr,(uint8_t *)"\xAC\xA1\x00\x00\xD5",5);
|
||||
break;
|
||||
case 2:
|
||||
memcpy(hopping_frequency,(uint8_t *)"\x32\x37\x41\x46",4);
|
||||
memcpy(rx_tx_addr,(uint8_t *)"\x92\x45\x01\x00\xD5",5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DM002_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = DM002_BIND_COUNT;
|
||||
DM002_initialize_txid();
|
||||
DM002_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
182
Multiprotocol/DSM.ino
Normal file
182
Multiprotocol/DSM.ino
Normal file
@@ -0,0 +1,182 @@
|
||||
#if defined(DSM_CYRF6936_INO) || defined(DSM_RX_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
uint8_t sop_col;
|
||||
|
||||
const uint8_t PROGMEM DSM_pncodes[5][9][8] = {
|
||||
/* Note these are in order transmitted (LSB 1st) */
|
||||
{ /* Row 0 */
|
||||
/* Col 0 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8},
|
||||
/* Col 1 */ {0x88, 0x17, 0x13, 0x3B, 0x2D, 0xBF, 0x06, 0xD6},
|
||||
/* Col 2 */ {0xF1, 0x94, 0x30, 0x21, 0xA1, 0x1C, 0x88, 0xA9},
|
||||
/* Col 3 */ {0xD0, 0xD2, 0x8E, 0xBC, 0x82, 0x2F, 0xE3, 0xB4},
|
||||
/* Col 4 */ {0x8C, 0xFA, 0x47, 0x9B, 0x83, 0xA5, 0x66, 0xD0},
|
||||
/* Col 5 */ {0x07, 0xBD, 0x9F, 0x26, 0xC8, 0x31, 0x0F, 0xB8},
|
||||
/* Col 6 */ {0xEF, 0x03, 0x95, 0x89, 0xB4, 0x71, 0x61, 0x9D},
|
||||
/* Col 7 */ {0x40, 0xBA, 0x97, 0xD5, 0x86, 0x4F, 0xCC, 0xD1},
|
||||
/* Col 8 */ {0xD7, 0xA1, 0x54, 0xB1, 0x5E, 0x89, 0xAE, 0x86}
|
||||
},
|
||||
{ /* Row 1 */
|
||||
/* Col 0 */ {0x83, 0xF7, 0xA8, 0x2D, 0x7A, 0x44, 0x64, 0xD3},
|
||||
/* Col 1 */ {0x3F, 0x2C, 0x4E, 0xAA, 0x71, 0x48, 0x7A, 0xC9},
|
||||
/* Col 2 */ {0x17, 0xFF, 0x9E, 0x21, 0x36, 0x90, 0xC7, 0x82},
|
||||
/* Col 3 */ {0xBC, 0x5D, 0x9A, 0x5B, 0xEE, 0x7F, 0x42, 0xEB},
|
||||
/* Col 4 */ {0x24, 0xF5, 0xDD, 0xF8, 0x7A, 0x77, 0x74, 0xE7},
|
||||
/* Col 5 */ {0x3D, 0x70, 0x7C, 0x94, 0xDC, 0x84, 0xAD, 0x95},
|
||||
/* Col 6 */ {0x1E, 0x6A, 0xF0, 0x37, 0x52, 0x7B, 0x11, 0xD4},
|
||||
/* Col 7 */ {0x62, 0xF5, 0x2B, 0xAA, 0xFC, 0x33, 0xBF, 0xAF},
|
||||
/* Col 8 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97}
|
||||
},
|
||||
{ /* Row 2 */
|
||||
/* Col 0 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97},
|
||||
/* Col 1 */ {0x8E, 0x4A, 0xD0, 0xA9, 0xA7, 0xFF, 0x20, 0xCA},
|
||||
/* Col 2 */ {0x4C, 0x97, 0x9D, 0xBF, 0xB8, 0x3D, 0xB5, 0xBE},
|
||||
/* Col 3 */ {0x0C, 0x5D, 0x24, 0x30, 0x9F, 0xCA, 0x6D, 0xBD},
|
||||
/* Col 4 */ {0x50, 0x14, 0x33, 0xDE, 0xF1, 0x78, 0x95, 0xAD},
|
||||
/* Col 5 */ {0x0C, 0x3C, 0xFA, 0xF9, 0xF0, 0xF2, 0x10, 0xC9},
|
||||
/* Col 6 */ {0xF4, 0xDA, 0x06, 0xDB, 0xBF, 0x4E, 0x6F, 0xB3},
|
||||
/* Col 7 */ {0x9E, 0x08, 0xD1, 0xAE, 0x59, 0x5E, 0xE8, 0xF0},
|
||||
/* Col 8 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E}
|
||||
},
|
||||
{ /* Row 3 */
|
||||
/* Col 0 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E},
|
||||
/* Col 1 */ {0x80, 0x69, 0x26, 0x80, 0x08, 0xF8, 0x49, 0xE7},
|
||||
/* Col 2 */ {0x7D, 0x2D, 0x49, 0x54, 0xD0, 0x80, 0x40, 0xC1},
|
||||
/* Col 3 */ {0xB6, 0xF2, 0xE6, 0x1B, 0x80, 0x5A, 0x36, 0xB4},
|
||||
/* Col 4 */ {0x42, 0xAE, 0x9C, 0x1C, 0xDA, 0x67, 0x05, 0xF6},
|
||||
/* Col 5 */ {0x9B, 0x75, 0xF7, 0xE0, 0x14, 0x8D, 0xB5, 0x80},
|
||||
/* Col 6 */ {0xBF, 0x54, 0x98, 0xB9, 0xB7, 0x30, 0x5A, 0x88},
|
||||
/* Col 7 */ {0x35, 0xD1, 0xFC, 0x97, 0x23, 0xD4, 0xC9, 0x88},
|
||||
/* Col 8 */ {0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40, 0x93}
|
||||
// Wrong values used by Orange TX/RX
|
||||
// /* Col 8 */ {0x88, 0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40}
|
||||
},
|
||||
{ /* Row 4 */
|
||||
/* Col 0 */ {0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40, 0x93},
|
||||
/* Col 1 */ {0xDC, 0x68, 0x08, 0x99, 0x97, 0xAE, 0xAF, 0x8C},
|
||||
/* Col 2 */ {0xC3, 0x0E, 0x01, 0x16, 0x0E, 0x32, 0x06, 0xBA},
|
||||
/* Col 3 */ {0xE0, 0x83, 0x01, 0xFA, 0xAB, 0x3E, 0x8F, 0xAC},
|
||||
/* Col 4 */ {0x5C, 0xD5, 0x9C, 0xB8, 0x46, 0x9C, 0x7D, 0x84},
|
||||
/* Col 5 */ {0xF1, 0xC6, 0xFE, 0x5C, 0x9D, 0xA5, 0x4F, 0xB7},
|
||||
/* Col 6 */ {0x58, 0xB5, 0xB3, 0xDD, 0x0E, 0x28, 0xF1, 0xB0},
|
||||
/* Col 7 */ {0x5F, 0x30, 0x3B, 0x56, 0x96, 0x45, 0xF4, 0xA1},
|
||||
/* Col 8 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8}
|
||||
},
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DSM_read_code(uint8_t *buf, uint8_t row, uint8_t col, uint8_t len)
|
||||
{
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
buf[i]=pgm_read_byte_near( &DSM_pncodes[row][col][i] );
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM DSM_init_vals[][2] = {
|
||||
{CYRF_02_TX_CTRL, 0x00}, // All TX interrupt disabled
|
||||
{CYRF_05_RX_CTRL, 0x00}, // All RX interrupt disabled
|
||||
{CYRF_28_CLK_EN, 0x02}, // Force receive clock enable
|
||||
{CYRF_32_AUTO_CAL_TIME, 0x3c}, // Default init value
|
||||
{CYRF_35_AUTOCAL_OFFSET, 0x14}, // Default init value
|
||||
{CYRF_26_XTAL_CFG, 0x08}, // Start delay
|
||||
{CYRF_06_RX_CFG, 0x4A}, // LNA enabled, RX override enabled, Fast turn mode enabled, RX is 1MHz below TX
|
||||
{CYRF_1B_TX_OFFSET_LSB, 0x55}, // Default init value
|
||||
{CYRF_1C_TX_OFFSET_MSB, 0x05}, // Default init value
|
||||
{CYRF_39_ANALOG_CTRL, 0x01}, // All slow for synth setting time
|
||||
{CYRF_01_TX_LENGTH, 0x10}, // 16 bytes packet
|
||||
{CYRF_14_EOP_CTRL, 0x02}, // Set EOP Symbol Count to 2
|
||||
{CYRF_12_DATA64_THOLD, 0x0a}, // 64 Chip Data PN corelator threshold, default datasheet value is 0x0E
|
||||
//Below is for bind only
|
||||
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER}, //64 chip codes, SDR mode
|
||||
{CYRF_10_FRAMING_CFG, 0x4a}, // SOP disabled, no LEN field and SOP correlator of 0x0a but since SOP is disabled...
|
||||
{CYRF_1F_TX_OVERRIDE, 0x04}, // Disable TX CRC, no ACK, use TX synthesizer
|
||||
{CYRF_1E_RX_OVERRIDE, 0x14}, // Disable RX CRC, Force receive data rate, use RX synthesizer
|
||||
};
|
||||
|
||||
const uint8_t PROGMEM DSM_data_vals[][2] = {
|
||||
{CYRF_29_RX_ABORT, 0x20}, // Abort RX operation in case we are coming from bind
|
||||
{CYRF_0F_XACT_CFG, 0x24}, // Force Idle
|
||||
{CYRF_29_RX_ABORT, 0x00}, // Clear abort RX
|
||||
{CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER}, // 64 chip codes, 8DR mode
|
||||
{CYRF_10_FRAMING_CFG, 0xea}, // SOP enabled, SOP_CODE_ADR 64 chips, Packet len enabled, SOP correlator 0x0A
|
||||
{CYRF_1F_TX_OVERRIDE, 0x00}, // CRC16 enabled, no ACK
|
||||
{CYRF_1E_RX_OVERRIDE, 0x00}, // CRC16 enabled, no ACK
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DSM_cyrf_config()
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(DSM_init_vals) / 2; i++)
|
||||
CYRF_WriteRegister(pgm_read_byte_near(&DSM_init_vals[i][0]), pgm_read_byte_near(&DSM_init_vals[i][1]));
|
||||
CYRF_WritePreamble(0x333304);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_cyrf_configdata()
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(DSM_data_vals) / 2; i++)
|
||||
CYRF_WriteRegister(pgm_read_byte_near(&DSM_data_vals[i][0]), pgm_read_byte_near(&DSM_data_vals[i][1]));
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) DSM_get_pn_row(uint8_t channel, bool dsmx)
|
||||
{
|
||||
return (dsmx ? (channel - 2) % 5 : channel % 5);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_set_sop_data_crc(bool ch2, bool dsmx)
|
||||
{
|
||||
//The crc for channel '1' is NOT(mfgid[0] << 8 + mfgid[1])
|
||||
//The crc for channel '2' is (mfgid[0] << 8 + mfgid[1])
|
||||
if(ch2)
|
||||
CYRF_ConfigCRCSeed(seed); //CH2
|
||||
else
|
||||
CYRF_ConfigCRCSeed(~seed); //CH1
|
||||
|
||||
uint8_t pn_row = DSM_get_pn_row(hopping_frequency[hopping_frequency_no], dsmx);
|
||||
uint8_t code[16];
|
||||
DSM_read_code(code,pn_row,sop_col,8); // pn_row between 0 and 4, sop_col between 1 and 7
|
||||
CYRF_ConfigSOPCode(code);
|
||||
DSM_read_code(code,pn_row,7 - sop_col,8); // 7-sop_col between 0 and 6
|
||||
DSM_read_code(code+8,pn_row,7 - sop_col + 1,8); // 7-sop_col+1 between 1 and 7
|
||||
CYRF_ConfigDataCode(code, 16);
|
||||
|
||||
CYRF_ConfigRFChannel(hopping_frequency[hopping_frequency_no]);
|
||||
hopping_frequency_no++;
|
||||
if(dsmx)
|
||||
hopping_frequency_no %=23;
|
||||
else
|
||||
hopping_frequency_no %=2;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_calc_dsmx_channel()
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
uint32_t id = ~(((uint32_t)cyrfmfg_id[0] << 24) | ((uint32_t)cyrfmfg_id[1] << 16) | ((uint32_t)cyrfmfg_id[2] << 8) | (cyrfmfg_id[3] << 0));
|
||||
uint32_t id_tmp = id;
|
||||
while(idx < 23)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t count_3_27 = 0, count_28_51 = 0, count_52_76 = 0;
|
||||
id_tmp = id_tmp * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
uint8_t next_ch = ((id_tmp >> 8) % 0x49) + 3; // Use least-significant byte and must be larger than 3
|
||||
if ( (next_ch ^ cyrfmfg_id[3]) & 0x01 )
|
||||
continue;
|
||||
for (i = 0; i < idx; i++)
|
||||
{
|
||||
if(hopping_frequency[i] == next_ch)
|
||||
break;
|
||||
if(hopping_frequency[i] <= 27)
|
||||
count_3_27++;
|
||||
else
|
||||
if (hopping_frequency[i] <= 51)
|
||||
count_28_51++;
|
||||
else
|
||||
count_52_76++;
|
||||
}
|
||||
if (i != idx)
|
||||
continue;
|
||||
if ((next_ch < 28 && count_3_27 < 8)
|
||||
||(next_ch >= 28 && next_ch < 52 && count_28_51 < 7)
|
||||
||(next_ch >= 52 && count_52_76 < 8))
|
||||
hopping_frequency[idx++] = next_ch;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,535 +0,0 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(DSM2_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
#define DSM2_NUM_CHANNELS 7
|
||||
#define RANDOM_CHANNELS 0 // disabled
|
||||
//#define RANDOM_CHANNELS 1 // enabled
|
||||
#define BIND_CHANNEL 0x0d //13 This can be any odd channel
|
||||
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
|
||||
|
||||
//During binding we will send BIND_COUNT/2 packets
|
||||
//One packet each 10msec
|
||||
#define BIND_COUNT1 600
|
||||
|
||||
enum {
|
||||
DSM2_BIND = 0,
|
||||
DSM2_CHANSEL = BIND_COUNT1 + 0,
|
||||
DSM2_CH1_WRITE_A = BIND_COUNT1 + 1,
|
||||
DSM2_CH1_CHECK_A = BIND_COUNT1 + 2,
|
||||
DSM2_CH2_WRITE_A = BIND_COUNT1 + 3,
|
||||
DSM2_CH2_CHECK_A = BIND_COUNT1 + 4,
|
||||
DSM2_CH2_READ_A = BIND_COUNT1 + 5,
|
||||
DSM2_CH1_WRITE_B = BIND_COUNT1 + 6,
|
||||
DSM2_CH1_CHECK_B = BIND_COUNT1 + 7,
|
||||
DSM2_CH2_WRITE_B = BIND_COUNT1 + 8,
|
||||
DSM2_CH2_CHECK_B = BIND_COUNT1 + 9,
|
||||
DSM2_CH2_READ_B = BIND_COUNT1 + 10,
|
||||
};
|
||||
|
||||
|
||||
const uint8_t pncodes[5][9][8] = {
|
||||
/* Note these are in order transmitted (LSB 1st) */
|
||||
{ /* Row 0 */
|
||||
/* Col 0 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8},
|
||||
/* Col 1 */ {0x88, 0x17, 0x13, 0x3B, 0x2D, 0xBF, 0x06, 0xD6},
|
||||
/* Col 2 */ {0xF1, 0x94, 0x30, 0x21, 0xA1, 0x1C, 0x88, 0xA9},
|
||||
/* Col 3 */ {0xD0, 0xD2, 0x8E, 0xBC, 0x82, 0x2F, 0xE3, 0xB4},
|
||||
/* Col 4 */ {0x8C, 0xFA, 0x47, 0x9B, 0x83, 0xA5, 0x66, 0xD0},
|
||||
/* Col 5 */ {0x07, 0xBD, 0x9F, 0x26, 0xC8, 0x31, 0x0F, 0xB8},
|
||||
/* Col 6 */ {0xEF, 0x03, 0x95, 0x89, 0xB4, 0x71, 0x61, 0x9D},
|
||||
/* Col 7 */ {0x40, 0xBA, 0x97, 0xD5, 0x86, 0x4F, 0xCC, 0xD1},
|
||||
/* Col 8 */ {0xD7, 0xA1, 0x54, 0xB1, 0x5E, 0x89, 0xAE, 0x86}
|
||||
},
|
||||
{ /* Row 1 */
|
||||
/* Col 0 */ {0x83, 0xF7, 0xA8, 0x2D, 0x7A, 0x44, 0x64, 0xD3},
|
||||
/* Col 1 */ {0x3F, 0x2C, 0x4E, 0xAA, 0x71, 0x48, 0x7A, 0xC9},
|
||||
/* Col 2 */ {0x17, 0xFF, 0x9E, 0x21, 0x36, 0x90, 0xC7, 0x82},
|
||||
/* Col 3 */ {0xBC, 0x5D, 0x9A, 0x5B, 0xEE, 0x7F, 0x42, 0xEB},
|
||||
/* Col 4 */ {0x24, 0xF5, 0xDD, 0xF8, 0x7A, 0x77, 0x74, 0xE7},
|
||||
/* Col 5 */ {0x3D, 0x70, 0x7C, 0x94, 0xDC, 0x84, 0xAD, 0x95},
|
||||
/* Col 6 */ {0x1E, 0x6A, 0xF0, 0x37, 0x52, 0x7B, 0x11, 0xD4},
|
||||
/* Col 7 */ {0x62, 0xF5, 0x2B, 0xAA, 0xFC, 0x33, 0xBF, 0xAF},
|
||||
/* Col 8 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97}
|
||||
},
|
||||
{ /* Row 2 */
|
||||
/* Col 0 */ {0x40, 0x56, 0x32, 0xD9, 0x0F, 0xD9, 0x5D, 0x97},
|
||||
/* Col 1 */ {0x8E, 0x4A, 0xD0, 0xA9, 0xA7, 0xFF, 0x20, 0xCA},
|
||||
/* Col 2 */ {0x4C, 0x97, 0x9D, 0xBF, 0xB8, 0x3D, 0xB5, 0xBE},
|
||||
/* Col 3 */ {0x0C, 0x5D, 0x24, 0x30, 0x9F, 0xCA, 0x6D, 0xBD},
|
||||
/* Col 4 */ {0x50, 0x14, 0x33, 0xDE, 0xF1, 0x78, 0x95, 0xAD},
|
||||
/* Col 5 */ {0x0C, 0x3C, 0xFA, 0xF9, 0xF0, 0xF2, 0x10, 0xC9},
|
||||
/* Col 6 */ {0xF4, 0xDA, 0x06, 0xDB, 0xBF, 0x4E, 0x6F, 0xB3},
|
||||
/* Col 7 */ {0x9E, 0x08, 0xD1, 0xAE, 0x59, 0x5E, 0xE8, 0xF0},
|
||||
/* Col 8 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E}
|
||||
},
|
||||
{ /* Row 3 */
|
||||
/* Col 0 */ {0xC0, 0x90, 0x8F, 0xBB, 0x7C, 0x8E, 0x2B, 0x8E},
|
||||
/* Col 1 */ {0x80, 0x69, 0x26, 0x80, 0x08, 0xF8, 0x49, 0xE7},
|
||||
/* Col 2 */ {0x7D, 0x2D, 0x49, 0x54, 0xD0, 0x80, 0x40, 0xC1},
|
||||
/* Col 3 */ {0xB6, 0xF2, 0xE6, 0x1B, 0x80, 0x5A, 0x36, 0xB4},
|
||||
/* Col 4 */ {0x42, 0xAE, 0x9C, 0x1C, 0xDA, 0x67, 0x05, 0xF6},
|
||||
/* Col 5 */ {0x9B, 0x75, 0xF7, 0xE0, 0x14, 0x8D, 0xB5, 0x80},
|
||||
/* Col 6 */ {0xBF, 0x54, 0x98, 0xB9, 0xB7, 0x30, 0x5A, 0x88},
|
||||
/* Col 7 */ {0x35, 0xD1, 0xFC, 0x97, 0x23, 0xD4, 0xC9, 0x88},
|
||||
/* Col 8 */ {0x88, 0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40}
|
||||
},
|
||||
{ /* Row 4 */
|
||||
/* Col 0 */ {0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40, 0x93},
|
||||
/* Col 1 */ {0xDC, 0x68, 0x08, 0x99, 0x97, 0xAE, 0xAF, 0x8C},
|
||||
/* Col 2 */ {0xC3, 0x0E, 0x01, 0x16, 0x0E, 0x32, 0x06, 0xBA},
|
||||
/* Col 3 */ {0xE0, 0x83, 0x01, 0xFA, 0xAB, 0x3E, 0x8F, 0xAC},
|
||||
/* Col 4 */ {0x5C, 0xD5, 0x9C, 0xB8, 0x46, 0x9C, 0x7D, 0x84},
|
||||
/* Col 5 */ {0xF1, 0xC6, 0xFE, 0x5C, 0x9D, 0xA5, 0x4F, 0xB7},
|
||||
/* Col 6 */ {0x58, 0xB5, 0xB3, 0xDD, 0x0E, 0x28, 0xF1, 0xB0},
|
||||
/* Col 7 */ {0x5F, 0x30, 0x3B, 0x56, 0x96, 0x45, 0xF4, 0xA1},
|
||||
/* Col 8 */ {0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8}
|
||||
},
|
||||
};
|
||||
|
||||
//
|
||||
uint8_t chidx;
|
||||
uint8_t sop_col;
|
||||
uint8_t data_col;
|
||||
uint16_t cyrf_state;
|
||||
uint8_t crcidx;
|
||||
uint8_t binding;
|
||||
uint16_t crc;
|
||||
uint8_t model;
|
||||
|
||||
/*
|
||||
#ifdef USE_FIXED_MFGID
|
||||
const uint8_t cyrfmfg_id[6] = {0x5e, 0x28, 0xa3, 0x1b, 0x00, 0x00}; //dx8
|
||||
const uint8_t cyrfmfg_id[6] = {0xd4, 0x62, 0xd6, 0xad, 0xd3, 0xff}; //dx6i
|
||||
#else
|
||||
//uint8_t cyrfmfg_id[6];
|
||||
#endif
|
||||
*/
|
||||
|
||||
void build_bind_packet()
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t sum = 384 - 0x10;//
|
||||
packet[0] = crc >> 8;
|
||||
packet[1] = crc & 0xff;
|
||||
packet[2] = 0xff ^ cyrfmfg_id[2];
|
||||
packet[3] = (0xff ^ cyrfmfg_id[3]) + model;
|
||||
packet[4] = packet[0];
|
||||
packet[5] = packet[1];
|
||||
packet[6] = packet[2];
|
||||
packet[7] = packet[3];
|
||||
for(i = 0; i < 8; i++)
|
||||
sum += packet[i];
|
||||
packet[8] = sum >> 8;
|
||||
packet[9] = sum & 0xff;
|
||||
packet[10] = 0x01; //???
|
||||
packet[11] = DSM2_NUM_CHANNELS;
|
||||
if(sub_protocol==DSMX) //DSMX type
|
||||
packet[12] = 0xb2; // Telemetry off: packet[12] = num_channels < 8 && Model.proto_opts[PROTOOPTS_TELEMETRY] == TELEM_OFF ? 0xa2 : 0xb2;
|
||||
else
|
||||
#if DSM2_NUM_CHANNELS < 8
|
||||
packet[12] = 0x01;
|
||||
#else
|
||||
packet[12] = 0x02;
|
||||
#endif
|
||||
packet[13] = 0x00; //???
|
||||
for(i = 8; i < 14; i++)
|
||||
sum += packet[i];
|
||||
packet[14] = sum >> 8;
|
||||
packet[15] = sum & 0xff;
|
||||
}
|
||||
|
||||
void build_data_packet(uint8_t upper)//
|
||||
{
|
||||
#if DSM2_NUM_CHANNELS==4
|
||||
const uint8_t ch_map[] = {0, 1, 2, 3, 0xff, 0xff, 0xff}; //Guess
|
||||
#elif DSM2_NUM_CHANNELS==5
|
||||
const uint8_t ch_map[] = {0, 1, 2, 3, 4, 0xff, 0xff}; //Guess
|
||||
#elif DSM2_NUM_CHANNELS==6
|
||||
const uint8_t ch_map[] = {1, 5, 2, 3, 0, 4, 0xff}; //HP6DSM
|
||||
#elif DSM2_NUM_CHANNELS==7
|
||||
const uint8_t ch_map[] = {1, 5, 2, 4, 3, 6, 0}; //DX6i
|
||||
#elif DSM2_NUM_CHANNELS==8
|
||||
const uint8_t ch_map[] = {1, 5, 2, 3, 6, 0xff, 0xff, 4, 0, 7, 0xff, 0xff, 0xff, 0xff}; //DX8
|
||||
#elif DSM2_NUM_CHANNELS==9
|
||||
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 0xff, 0xff, 0xff, 0xff, 0xff}; //DM9
|
||||
#elif DSM2_NUM_CHANNELS==10
|
||||
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff};
|
||||
#elif DSM2_NUM_CHANNELS==11
|
||||
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 10, 0xff, 0xff, 0xff};
|
||||
#elif DSM2_NUM_CHANNELS==12
|
||||
const uint8_t ch_map[] = {3, 2, 1, 5, 0, 4, 6, 7, 8, 9, 10, 11, 0xff, 0xff};
|
||||
#endif
|
||||
|
||||
uint8_t i;
|
||||
uint8_t bits;
|
||||
//
|
||||
if( binding && PROTOCOL_SticksMoved(0) )
|
||||
{
|
||||
//BIND_DONE;
|
||||
binding = 0;
|
||||
}
|
||||
if (sub_protocol==DSMX)
|
||||
{
|
||||
packet[0] = cyrfmfg_id[2];
|
||||
packet[1] = cyrfmfg_id[3] + model;
|
||||
bits=11;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[0] = (0xff ^ cyrfmfg_id[2]);
|
||||
packet[1] = (0xff ^ cyrfmfg_id[3]) + model;
|
||||
bits=10;
|
||||
}
|
||||
//
|
||||
uint16_t max = 1 << bits;//max=2048 for DSMX & 1024 for DSM2 less than 8 ch and 2048 otherwise
|
||||
//uint16_t pct_100 = (uint32_t)max * 100 / 150;//682 1024*100/150
|
||||
//
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
uint8_t idx = ch_map[upper * 7 + i];//1,5,2,3,0,4
|
||||
uint16_t value;
|
||||
if (idx == 0xff)
|
||||
value = 0xffff;
|
||||
else
|
||||
{
|
||||
if (binding)
|
||||
{ // Failsafe position during binding
|
||||
value=max/2; //all channels to middle
|
||||
if(idx==0)
|
||||
value=1; //except throttle
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(idx)
|
||||
{
|
||||
case 0:
|
||||
value=Servo_data[THROTTLE];//85.75-938.25=125%//171-853=100%
|
||||
break;
|
||||
case 1:
|
||||
value=Servo_data[AILERON];
|
||||
break;
|
||||
case 2:
|
||||
value=Servo_data[ELEVATOR];
|
||||
break;
|
||||
case 3:
|
||||
value=Servo_data[RUDDER];
|
||||
break;
|
||||
case 4:
|
||||
value=Servo_data[AUX1];
|
||||
break;
|
||||
case 5:
|
||||
value=Servo_data[AUX2];
|
||||
break;
|
||||
case 6:
|
||||
value=Servo_data[AUX3];
|
||||
break;
|
||||
case 7:
|
||||
value=Servo_data[AUX4];
|
||||
break;
|
||||
}
|
||||
value=map(value,PPM_MIN,PPM_MAX,0,max-1);
|
||||
}
|
||||
value |= (upper && i == 0 ? 0x8000 : 0) | (idx << bits);
|
||||
}
|
||||
packet[i*2+2] = (value >> 8) & 0xff;
|
||||
packet[i*2+3] = (value >> 0) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PROTOCOL_SticksMoved(uint8_t init)
|
||||
{
|
||||
#define STICK_MOVEMENT 15*(PPM_MAX-PPM_MIN)/100 // defines when the bind dialog should be interrupted (stick movement STICK_MOVEMENT %)
|
||||
static uint16_t ele_start, ail_start;
|
||||
uint16_t ele = Servo_data[ELEVATOR];//CHAN_ReadInput(MIXER_MapChannel(INP_ELEVATOR));
|
||||
uint16_t ail = Servo_data[AILERON];//CHAN_ReadInput(MIXER_MapChannel(INP_AILERON));
|
||||
if(init) {
|
||||
ele_start = ele;
|
||||
ail_start = ail;
|
||||
return 0;
|
||||
}
|
||||
uint16_t ele_diff = ele_start - ele;//abs(ele_start - ele);
|
||||
uint16_t ail_diff = ail_start - ail;//abs(ail_start - ail);
|
||||
return ((ele_diff + ail_diff) > STICK_MOVEMENT);//
|
||||
}
|
||||
|
||||
uint8_t get_pn_row(uint8_t channel)
|
||||
{
|
||||
return (sub_protocol == DSMX ? (channel - 2) % 5 : channel % 5);
|
||||
}
|
||||
|
||||
const uint8_t init_vals[][2] = {
|
||||
{CYRF_02_TX_CTRL, 0x00},
|
||||
{CYRF_05_RX_CTRL, 0x00},
|
||||
{CYRF_28_CLK_EN, 0x02},
|
||||
{CYRF_32_AUTO_CAL_TIME, 0x3c},
|
||||
{CYRF_35_AUTOCAL_OFFSET, 0x14},
|
||||
{CYRF_06_RX_CFG, 0x4A},
|
||||
{CYRF_1B_TX_OFFSET_LSB, 0x55},
|
||||
{CYRF_1C_TX_OFFSET_MSB, 0x05},
|
||||
{CYRF_0F_XACT_CFG, 0x24}, // Force Idle
|
||||
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER}, //Set 64chip, SDR mode
|
||||
{CYRF_12_DATA64_THOLD, 0x0a},
|
||||
{CYRF_0F_XACT_CFG, 0x04}, // Idle
|
||||
{CYRF_39_ANALOG_CTRL, 0x01},
|
||||
{CYRF_0F_XACT_CFG, 0x24}, //Force IDLE
|
||||
{CYRF_29_RX_ABORT, 0x00}, //Clear RX abort
|
||||
{CYRF_12_DATA64_THOLD, 0x0a}, //set pn correlation threshold
|
||||
{CYRF_10_FRAMING_CFG, 0x4a}, //set sop len and threshold
|
||||
{CYRF_29_RX_ABORT, 0x0f}, //Clear RX abort?
|
||||
{CYRF_03_TX_CFG, 0x38 | CYRF_BIND_POWER}, //Set 64chip, SDR mode
|
||||
{CYRF_10_FRAMING_CFG, 0x4a}, //set sop len and threshold
|
||||
{CYRF_1F_TX_OVERRIDE, 0x04}, //disable tx CRC
|
||||
{CYRF_1E_RX_OVERRIDE, 0x14}, //disable rx crc
|
||||
{CYRF_14_EOP_CTRL, 0x02}, //set EOP sync == 2
|
||||
{CYRF_01_TX_LENGTH, 0x10}, //16byte packet
|
||||
};
|
||||
|
||||
void cyrf_config()
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(init_vals) / 2; i++)
|
||||
CYRF_WriteRegister(init_vals[i][0], init_vals[i][1]);
|
||||
CYRF_WritePreamble(0x333304);
|
||||
CYRF_ConfigRFChannel(0x61);
|
||||
}
|
||||
|
||||
void initialize_bind_state()
|
||||
{
|
||||
const uint8_t pn_bind[] = { 0xc6,0x94,0x22,0xfe,0x48,0xe6,0x57,0x4e };
|
||||
uint8_t data_code[32];
|
||||
CYRF_ConfigRFChannel(BIND_CHANNEL); //This seems to be random?
|
||||
uint8_t pn_row = get_pn_row(BIND_CHANNEL);
|
||||
//printf("Ch: %d Row: %d SOP: %d Data: %d\n", BIND_CHANNEL, pn_row, sop_col, data_col);
|
||||
CYRF_ConfigCRCSeed(crc);
|
||||
CYRF_ConfigSOPCode(pncodes[pn_row][sop_col]);
|
||||
memcpy(data_code, pncodes[pn_row][data_col], 16);
|
||||
memcpy(data_code + 16, pncodes[0][8], 8);
|
||||
memcpy(data_code + 24, pn_bind, 8);
|
||||
CYRF_ConfigDataCode(data_code, 32);
|
||||
build_bind_packet();
|
||||
}
|
||||
|
||||
const uint8_t data_vals[][2] = {
|
||||
{CYRF_05_RX_CTRL, 0x83}, //Initialize for reading RSSI
|
||||
{CYRF_29_RX_ABORT, 0x20},
|
||||
{CYRF_0F_XACT_CFG, 0x24},
|
||||
{CYRF_29_RX_ABORT, 0x00},
|
||||
{CYRF_03_TX_CFG, 0x08 | CYRF_HIGH_POWER},
|
||||
{CYRF_10_FRAMING_CFG, 0xea},
|
||||
{CYRF_1F_TX_OVERRIDE, 0x00},
|
||||
{CYRF_1E_RX_OVERRIDE, 0x00},
|
||||
{CYRF_03_TX_CFG, 0x28 | CYRF_HIGH_POWER},
|
||||
{CYRF_12_DATA64_THOLD, 0x3f},
|
||||
{CYRF_10_FRAMING_CFG, 0xff},
|
||||
{CYRF_0F_XACT_CFG, 0x24}, //Switch from reading RSSI to Writing
|
||||
{CYRF_29_RX_ABORT, 0x00},
|
||||
{CYRF_12_DATA64_THOLD, 0x0a},
|
||||
{CYRF_10_FRAMING_CFG, 0xea},
|
||||
};
|
||||
|
||||
void cyrf_configdata()
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(data_vals) / 2; i++)
|
||||
CYRF_WriteRegister(data_vals[i][0], data_vals[i][1]);
|
||||
}
|
||||
|
||||
void set_sop_data_crc()
|
||||
{
|
||||
uint8_t pn_row = get_pn_row(hopping_frequency[chidx]);
|
||||
//printf("Ch: %d Row: %d SOP: %d Data: %d\n", ch[chidx], pn_row, sop_col, data_col);
|
||||
CYRF_ConfigRFChannel(hopping_frequency[chidx]);
|
||||
CYRF_ConfigCRCSeed(crcidx ? ~crc : crc);
|
||||
CYRF_ConfigSOPCode(pncodes[pn_row][sop_col]);
|
||||
CYRF_ConfigDataCode(pncodes[pn_row][data_col], 16);
|
||||
if(sub_protocol == DSMX)
|
||||
chidx = (chidx + 1) % 23;
|
||||
else
|
||||
chidx = (chidx + 1) % 2;
|
||||
crcidx = !crcidx;
|
||||
}
|
||||
|
||||
void calc_dsmx_channel()
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
uint32_t id = ~(((uint32_t)cyrfmfg_id[0] << 24) | ((uint32_t)cyrfmfg_id[1] << 16) | ((uint32_t)cyrfmfg_id[2] << 8) | (cyrfmfg_id[3] << 0));
|
||||
uint32_t id_tmp = id;
|
||||
while(idx < 23)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t count_3_27 = 0, count_28_51 = 0, count_52_76 = 0;
|
||||
id_tmp = id_tmp * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
uint8_t next_ch = ((id_tmp >> 8) % 0x49) + 3; // Use least-significant byte and must be larger than 3
|
||||
if (((next_ch ^ id) & 0x01 )== 0)
|
||||
continue;
|
||||
for (i = 0; i < idx; i++)
|
||||
{
|
||||
if(hopping_frequency[i] == next_ch)
|
||||
break;
|
||||
if(hopping_frequency[i] <= 27)
|
||||
count_3_27++;
|
||||
else
|
||||
if (hopping_frequency[i] <= 51)
|
||||
count_28_51++;
|
||||
else
|
||||
count_52_76++;
|
||||
}
|
||||
if (i != idx)
|
||||
continue;
|
||||
if ((next_ch < 28 && count_3_27 < 8)
|
||||
||(next_ch >= 28 && next_ch < 52 && count_28_51 < 7)
|
||||
||(next_ch >= 52 && count_52_76 < 8))
|
||||
hopping_frequency[idx++] = next_ch;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ReadDsm2()
|
||||
{
|
||||
#define CH1_CH2_DELAY 4010 // Time between write of channel 1 and channel 2
|
||||
#define WRITE_DELAY 1650 // 1550 original, Time after write to verify write complete
|
||||
#define READ_DELAY 400 // Time before write to check read state, and switch channels
|
||||
uint8_t i = 0;
|
||||
|
||||
switch(cyrf_state)
|
||||
{
|
||||
default:
|
||||
//Binding
|
||||
cyrf_state++;
|
||||
if(cyrf_state & 1)
|
||||
{
|
||||
//Send packet on even states
|
||||
//Note state has already incremented,
|
||||
// so this is actually 'even' state
|
||||
CYRF_WriteDataPacket(packet);
|
||||
return 8500;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Check status on odd states
|
||||
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS);
|
||||
return 1500;
|
||||
}
|
||||
case DSM2_CHANSEL:
|
||||
BIND_DONE;
|
||||
//Select channels and configure for writing data
|
||||
//CYRF_FindBestChannels(ch, 2, 10, 1, 79);
|
||||
cyrf_configdata();
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
chidx = 0;
|
||||
crcidx = 0;
|
||||
cyrf_state = DSM2_CH1_WRITE_A; // in fact cyrf_state++
|
||||
set_sop_data_crc();
|
||||
return 10000;
|
||||
case DSM2_CH1_WRITE_A:
|
||||
case DSM2_CH1_WRITE_B:
|
||||
build_data_packet(cyrf_state == DSM2_CH1_WRITE_B);//compare state and DSM2_CH1_WRITE_B return 0 or 1
|
||||
case DSM2_CH2_WRITE_A:
|
||||
case DSM2_CH2_WRITE_B:
|
||||
CYRF_WriteDataPacket(packet);
|
||||
cyrf_state++; // change from WRITE to CHECK mode
|
||||
return WRITE_DELAY;
|
||||
case DSM2_CH1_CHECK_A:
|
||||
case DSM2_CH1_CHECK_B:
|
||||
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
|
||||
if(++i > NUM_WAIT_LOOPS)
|
||||
break;
|
||||
set_sop_data_crc();
|
||||
cyrf_state++; // change from CH1_CHECK to CH2_WRITE
|
||||
return CH1_CH2_DELAY - WRITE_DELAY;
|
||||
case DSM2_CH2_CHECK_A:
|
||||
case DSM2_CH2_CHECK_B:
|
||||
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
|
||||
if(++i > NUM_WAIT_LOOPS)
|
||||
break;
|
||||
if (cyrf_state == DSM2_CH2_CHECK_A)
|
||||
CYRF_SetPower(0x28); //Keep transmit power in sync
|
||||
// No telemetry...
|
||||
set_sop_data_crc();
|
||||
if (cyrf_state == DSM2_CH2_CHECK_A)
|
||||
{
|
||||
#if DSM2_NUM_CHANNELS < 8
|
||||
cyrf_state = DSM2_CH1_WRITE_A; // change from CH2_CHECK_A to CH1_WRITE_A (ie no upper)
|
||||
return 11000 - CH1_CH2_DELAY - WRITE_DELAY ; // Original is 22000 from deviation but it works better this way
|
||||
#else
|
||||
cyrf_state = DSM2_CH1_WRITE_B; // change from CH2_CHECK_A to CH1_WRITE_A (to transmit upper)
|
||||
#endif
|
||||
}
|
||||
else
|
||||
cyrf_state = DSM2_CH1_WRITE_A; // change from CH2_CHECK_B to CH1_WRITE_A (upper already transmitted so transmit lower)
|
||||
return 11000 - CH1_CH2_DELAY - WRITE_DELAY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t initDsm2()
|
||||
{
|
||||
CYRF_Reset();
|
||||
CYRF_GetMfgData(cyrfmfg_id);//
|
||||
|
||||
cyrf_config();
|
||||
|
||||
if (sub_protocol ==DSMX)
|
||||
calc_dsmx_channel();
|
||||
else
|
||||
{
|
||||
#if RANDOM_CHANNELS == 1
|
||||
uint8_t tmpch[10];
|
||||
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75);
|
||||
//
|
||||
randomSeed((uint32_t)analogRead(A6)<<10|analogRead(A7));//seed
|
||||
uint8_t idx = random(0xfefefefe) % 10;
|
||||
hopping_frequency[0] = tmpch[idx];
|
||||
while(1)
|
||||
{
|
||||
idx = random(0xfefefefe) % 10;
|
||||
if (tmpch[idx] != hopping_frequency[0])
|
||||
break;
|
||||
}
|
||||
hopping_frequency[1] = tmpch[idx];
|
||||
#else
|
||||
hopping_frequency[0] = (cyrfmfg_id[0] + cyrfmfg_id[2] + cyrfmfg_id[4]) % 39 + 1;
|
||||
hopping_frequency[1] = (cyrfmfg_id[1] + cyrfmfg_id[3] + cyrfmfg_id[5]) % 40 + 40;
|
||||
#endif
|
||||
}
|
||||
|
||||
///}
|
||||
crc = ~((cyrfmfg_id[0] << 8) + cyrfmfg_id[1]); //The crc for channel 'a' is NOT(mfgid[1] << 8 + mfgid[0])
|
||||
crcidx = 0;//The crc for channel 'b' is (mfgid[1] << 8 + mfgid[0])
|
||||
//
|
||||
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;//Ok
|
||||
data_col = 7 - sop_col;//ok
|
||||
|
||||
model=MProtocol_id-MProtocol_id_master; // RxNum for serial or 0 for ppm
|
||||
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
//
|
||||
if(IS_AUTOBIND_FLAG_on)
|
||||
{
|
||||
cyrf_state = DSM2_BIND;
|
||||
PROTOCOL_SticksMoved(1); //Initialize Stick position
|
||||
initialize_bind_state();
|
||||
binding = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cyrf_state = DSM2_CHANSEL;//
|
||||
binding = 0;
|
||||
}
|
||||
return 10000;
|
||||
}
|
||||
|
||||
#endif
|
||||
508
Multiprotocol/DSM_Rx_cyrf6936.ino
Normal file
508
Multiprotocol/DSM_Rx_cyrf6936.ino
Normal file
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(DSM_RX_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
//#define DSM_DEBUG_RF
|
||||
//#define DSM_DEBUG_CH
|
||||
|
||||
uint8_t DSM_rx_type;
|
||||
|
||||
enum {
|
||||
DSM_RX_BIND1 = 0,
|
||||
DSM_RX_BIND2,
|
||||
DSM_RX_DATA_PREP,
|
||||
DSM2_RX_SCAN,
|
||||
DSM_RX_DATA_CH1,
|
||||
DSM_RX_DATA_CH2,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DSM_RX_RF_init()
|
||||
{
|
||||
DSM_cyrf_config();
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
//64 SDR Mode is configured so only the 8 first values are needed but need to write 16 values...
|
||||
uint8_t code[16];
|
||||
DSM_read_code(code,0,8,8);
|
||||
CYRF_ConfigDataCode(code, 16);
|
||||
CYRF_ConfigRFChannel(1);
|
||||
CYRF_SetTxRxMode(RX_EN); // Force end state read
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); // Prepare to receive
|
||||
}
|
||||
else
|
||||
{
|
||||
DSM_cyrf_configdata();
|
||||
CYRF_WriteRegister(CYRF_06_RX_CFG, rx_disable_lna ? 0x0A:0x4A); // AGC disabled, LNA disabled/enabled, Attenuator disabled, RX override enabled, Fast turn mode enabled, RX is 1MHz below TX
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t convert_channel_DSM_nolimit(int32_t val)
|
||||
{
|
||||
val=(val-0x150)*(CHANNEL_MAX_100-CHANNEL_MIN_100)/(0x6B0-0x150)+CHANNEL_MIN_100;
|
||||
if(val<0)
|
||||
val=0;
|
||||
else
|
||||
if(val>2047)
|
||||
val=2047;
|
||||
return (uint16_t)val;
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) DSM_RX_check_packet()
|
||||
{
|
||||
uint8_t rx_status=CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_status & 0x03) == 0x02) // RXC=1, RXE=0 then 2nd check is required (debouncing)
|
||||
rx_status |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_status & 0x07) == 0x02)
|
||||
{ // data received with no errors
|
||||
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("l=%d",len);
|
||||
#endif
|
||||
if(len>=2 && len<=16)
|
||||
{
|
||||
// Read packet
|
||||
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80); // Need to set RXOW before data read
|
||||
CYRF_ReadDataPacketLen(packet, len);
|
||||
|
||||
// Check packet ID
|
||||
if ((DSM_rx_type&0x80) == 0)
|
||||
{//DSM2
|
||||
packet[0] ^= 0xff;
|
||||
packet[1] ^= 0xff;
|
||||
}
|
||||
#ifdef DSM_DEBUG_CH
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
debug("%02X ",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
if(packet[0] == cyrfmfg_id[2] && packet[1] == cyrfmfg_id[3])
|
||||
return 0x02; // Packet ok
|
||||
}
|
||||
return 0x00; // Wrong size or ID -> nothing received
|
||||
}
|
||||
return rx_status; // Return error code
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_RX_build_telemetry_packet()
|
||||
{
|
||||
uint8_t nbr_bits = 11;
|
||||
if((DSM_rx_type&0xF0) == 0x00)
|
||||
nbr_bits=10; // Only DSM_22 is using a resolution of 1024
|
||||
|
||||
// Use packet length to calculate the number of channels
|
||||
len -= 2; // Remove header length
|
||||
len >>= 1; // Channels are on 2 bytes
|
||||
if(len==0) return; // No channels...
|
||||
|
||||
// Extract channels
|
||||
uint8_t idx;
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
uint16_t value=(packet[i*2+2]<<8) | packet[i*2+3];
|
||||
if(value!=0xFFFF)
|
||||
{
|
||||
idx=(value&0x7FFF)>>nbr_bits; // retrieve channel index
|
||||
#ifdef DSM_DEBUG_CH
|
||||
debugln("i=%d,v=%d,u=%X",idx,value&0x7FF,value&0x8000);
|
||||
#endif
|
||||
if(idx<13)
|
||||
{
|
||||
if(nbr_bits==10) value <<= 1; // switch to 11 bits
|
||||
value &= 0x7FF;
|
||||
rx_rc_chan[CH_TAER[idx]]=convert_channel_DSM_nolimit(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buid telemetry packet
|
||||
idx=0;
|
||||
packet_in[idx++] = RX_LQI;
|
||||
packet_in[idx++] = RX_LQI;
|
||||
packet_in[idx++] = 0; // start channel
|
||||
packet_in[idx++] = 12; // number of channels in packet
|
||||
|
||||
// Pack channels
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
for (uint8_t i = 0; i < 12; i++)
|
||||
{
|
||||
bits |= ((uint32_t)rx_rc_chan[i]) << bitsavailable;
|
||||
bitsavailable += 11;
|
||||
while (bitsavailable >= 8)
|
||||
{
|
||||
packet_in[idx++] = bits & 0xff;
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
if(bitsavailable)
|
||||
packet_in[idx++] = bits & 0xff;
|
||||
// Send telemetry
|
||||
telemetry_link = 1;
|
||||
#ifdef SEND_CPPM
|
||||
if(sub_protocol>0)
|
||||
telemetry_link |= 0x80; // Disable telemetry output
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool __attribute__((unused)) DSM_RX_bind_check_validity()
|
||||
{
|
||||
uint16_t sum = 384 - 0x10;//
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
sum += packet_in[i];
|
||||
if( packet_in[8] != (sum>>8) || packet_in[9] != (sum&0xFF)) //Checksum
|
||||
return false;
|
||||
for(uint8_t i = 8; i < 14; i++)
|
||||
sum += packet_in[i];
|
||||
if( packet_in[14] != (sum>>8) || packet_in[15] != (sum&0xFF)) //Checksum
|
||||
return false;
|
||||
if(memcmp(packet_in,packet_in+4,4)) //Check ID
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_RX_build_bind_packet()
|
||||
{
|
||||
uint16_t sum = 384 - 0x10;//
|
||||
packet[0] = 0xff ^ cyrfmfg_id[0]; // ID
|
||||
packet[1] = 0xff ^ cyrfmfg_id[1];
|
||||
packet[2] = 0xff ^ cyrfmfg_id[2];
|
||||
packet[3] = 0xff ^ cyrfmfg_id[3];
|
||||
packet[4] = 0x01; // RX version
|
||||
packet[5] = num_ch; // Number of channels
|
||||
packet[6] = DSM_rx_type; // DSM type, let's just send back whatever the TX gave us...
|
||||
packet[7] = 0x00; // Unknown
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
sum += packet[i];
|
||||
packet[8] = sum >> 8;
|
||||
packet[9] = sum & 0xff;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_abort_channel_rx(uint8_t ch)
|
||||
{
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
CYRF_SetTxRxMode(IS_POWER_FLAG_on ? TXRX_OFF:RX_EN); // Force end state read
|
||||
if (rx_disable_lna != IS_POWER_FLAG_on && IS_BIND_DONE)
|
||||
{
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
CYRF_WriteRegister(CYRF_06_RX_CFG, rx_disable_lna ? 0x0A:0x4A); // AGC disabled, LNA disabled/enabled, Attenuator disabled, RX override enabled, Fast turn mode enabled, RX is 1MHz below TX
|
||||
}
|
||||
if(ch&0x02) DSM_set_sop_data_crc(true ,DSM_rx_type&0x80); // Set sop data,crc seed and rf channel using CH1, DSM2/X
|
||||
if(ch&0x01) DSM_set_sop_data_crc(false,DSM_rx_type&0x80); // Set sop data,crc seed and rf channel using CH1, DSM2/X
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX operation
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); // Prepare to receive
|
||||
}
|
||||
|
||||
uint16_t DSM_RX_callback()
|
||||
{
|
||||
uint8_t rx_status;
|
||||
static uint8_t read_retry=0;
|
||||
|
||||
switch (phase)
|
||||
{
|
||||
case DSM_RX_BIND1:
|
||||
if(IS_BIND_DONE) // Abort bind
|
||||
{
|
||||
phase = DSM_RX_DATA_PREP;
|
||||
break;
|
||||
}
|
||||
if(packet_count==0)
|
||||
read_retry=0;
|
||||
//Check received data
|
||||
rx_status = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_status & 0x03) == 0x02) // RXC=1, RXE=0 then 2nd check is required (debouncing)
|
||||
rx_status |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_status & 0x07) == 0x02)
|
||||
{ // data received with no errors
|
||||
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80); // Need to set RXOW before data read
|
||||
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
debugln("RX:%d, CH:%d",len,hopping_frequency_no);
|
||||
if(len==16)
|
||||
{
|
||||
CYRF_ReadDataPacketLen(packet_in, 16);
|
||||
if(DSM_RX_bind_check_validity())
|
||||
{
|
||||
// store tx info into eeprom
|
||||
uint16_t temp = DSM_RX_EEPROM_OFFSET;
|
||||
debug("ID=");
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
{
|
||||
cyrfmfg_id[i]=packet_in[i]^0xFF;
|
||||
eeprom_write_byte((EE_ADDR)temp++, cyrfmfg_id[i]);
|
||||
debug(" %02X", cyrfmfg_id[i]);
|
||||
}
|
||||
// check num_ch
|
||||
num_ch=packet_in[11];
|
||||
if(num_ch>12) num_ch=12;
|
||||
//check DSM_rx_type
|
||||
/*packet[12] 1 byte -> max DSM type allowed:
|
||||
0x01 => 22ms 1024 DSM2 1 packet => number of channels is <8
|
||||
0x02 => 22ms 1024 DSM2 2 packets => either a number of channel >7
|
||||
0x12 => 11ms 2048 DSM2 2 packets => can be any number of channels
|
||||
0xA2 => 22ms 2048 DSMX 1 packet => number of channels is <8
|
||||
0xB2 => 11ms 2048 DSMX => can be any number of channels
|
||||
(0x01 or 0xA2) and num_ch < 7 => 22ms else 11ms
|
||||
&0x80 => false=DSM2, true=DSMX
|
||||
&0xF0 => false=1024, true=2048 */
|
||||
DSM_rx_type=packet_in[12];
|
||||
switch(DSM_rx_type)
|
||||
{
|
||||
case 0x01:
|
||||
if(num_ch>7) DSM_rx_type = 0x02; // Can't be 0x01 with this number of channels
|
||||
break;
|
||||
case 0xA2:
|
||||
if(num_ch>7) DSM_rx_type = 0xB2; // Can't be 0xA2 with this number of channels
|
||||
break;
|
||||
case 0x02:
|
||||
case 0x12:
|
||||
case 0xB2:
|
||||
break;
|
||||
default: // Unknown type, default to DSMX 11ms
|
||||
DSM_rx_type = 0xB2;
|
||||
break;
|
||||
}
|
||||
eeprom_write_byte((EE_ADDR)temp, DSM_rx_type);
|
||||
debugln(", num_ch=%d, type=%02X",num_ch, DSM_rx_type);
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
CYRF_SetTxRxMode(TX_EN); // Force end state TX
|
||||
CYRF_ConfigDataCode((const uint8_t *)"\x98\x88\x1B\xE4\x30\x79\x03\x84", 16);
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX
|
||||
DSM_RX_build_bind_packet();
|
||||
bind_counter=500;
|
||||
phase++; // DSM_RX_BIND2;
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
DSM_abort_channel_rx(0); // Abort RX operation and receive
|
||||
if(read_retry==0)
|
||||
read_retry=8;
|
||||
}
|
||||
else
|
||||
if(rx_status & 0x02) // RX error
|
||||
DSM_abort_channel_rx(0); // Abort RX operation and receive
|
||||
packet_count++;
|
||||
if(packet_count>12)
|
||||
{
|
||||
packet_count=1;
|
||||
if(read_retry)
|
||||
read_retry--;
|
||||
if(read_retry==0)
|
||||
{
|
||||
packet_count=0;
|
||||
hopping_frequency_no++; // Change channel
|
||||
hopping_frequency_no %= 0x50;
|
||||
hopping_frequency_no |= 0x01; // Odd channels only
|
||||
CYRF_ConfigRFChannel(hopping_frequency_no);
|
||||
DSM_abort_channel_rx(0); // Abort RX operation and receive
|
||||
}
|
||||
}
|
||||
return 1000;
|
||||
case DSM_RX_BIND2:
|
||||
//Transmit settings back
|
||||
CYRF_WriteDataPacketLen(packet,10); // Send packet
|
||||
if(bind_counter--==0)
|
||||
{
|
||||
BIND_DONE;
|
||||
phase++; // DSM_RX_DATA_PREP
|
||||
}
|
||||
break;
|
||||
case DSM_RX_DATA_PREP:
|
||||
hopping_frequency_no = 0;
|
||||
read_retry=0;
|
||||
rx_data_started = false;
|
||||
pps_counter = 0;
|
||||
RX_LQI = 100;
|
||||
DSM_cyrf_configdata();
|
||||
pps_timer=millis();
|
||||
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
|
||||
seed = (cyrfmfg_id[0] << 8) + cyrfmfg_id[1];
|
||||
if(DSM_rx_type&0x80)
|
||||
{ // DSMX
|
||||
DSM_calc_dsmx_channel(); // Build hop table
|
||||
DSM_abort_channel_rx(1); // Abort RX operation, set sop&data&seed&rf using CH1, DSM2/X and receive
|
||||
phase=DSM_RX_DATA_CH1;
|
||||
}
|
||||
else
|
||||
{ // DSM2
|
||||
rf_ch_num=0;
|
||||
hopping_frequency_no = 0;
|
||||
hopping_frequency[0] = 3;
|
||||
hopping_frequency[1] = 0;
|
||||
DSM_abort_channel_rx(1); // Abort RX operation, set sop&data&seed&rf using CH1, DSM2/X and receive
|
||||
phase=DSM2_RX_SCAN;
|
||||
}
|
||||
break;
|
||||
case DSM2_RX_SCAN: // Scan for DSM2 frequencies
|
||||
//Received something ?
|
||||
rx_status = DSM_RX_check_packet();
|
||||
if(rx_status == 0x02)
|
||||
{ // data received with no errors
|
||||
debugln("CH%d:Found %d",rf_ch_num+1,hopping_frequency[rf_ch_num]);
|
||||
read_retry=0;
|
||||
if(rf_ch_num)
|
||||
{ // Both CH1 and CH2 found
|
||||
read_retry=0;
|
||||
hopping_frequency_no=0;
|
||||
DSM_abort_channel_rx(1); // Abort RX operation, set sop&data&seed&rf using CH1, DSM2/X and receive
|
||||
pps_timer=millis();
|
||||
phase++; // DSM_RX_DATA_CH1
|
||||
}
|
||||
else
|
||||
{
|
||||
rf_ch_num++; // CH1 found, scan for CH2
|
||||
hopping_frequency_no = 1;
|
||||
if(hopping_frequency[1] < 3) // If no CH2 keep then restart from current
|
||||
hopping_frequency[1]=hopping_frequency[0]+1;
|
||||
DSM_abort_channel_rx(2); // Abort RX operation, set sop&data&seed&rf using CH2, DSM2/X and receive
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
read_retry++;
|
||||
if(read_retry>50) // After 50ms
|
||||
{ // Try next channel
|
||||
debugln("CH%d:Next channel",rf_ch_num+1);
|
||||
read_retry=0;
|
||||
hopping_frequency_no = rf_ch_num;
|
||||
hopping_frequency[rf_ch_num]++;
|
||||
if(hopping_frequency[rf_ch_num] > 73) hopping_frequency[rf_ch_num] = 3;
|
||||
DSM_abort_channel_rx(rf_ch_num+1); // Abort RX operation, set sop&data&seed&rf using CH1/2, DSM2/X and receive
|
||||
}
|
||||
else if(rx_status & 0x02)
|
||||
{ // data received with errors
|
||||
if((rx_status & 0x01) && rf_ch_num==0)
|
||||
hopping_frequency[1] = hopping_frequency[0];// Might be CH2 since it's a CRC error so keep it
|
||||
debugln("CH%d:RX error",rf_ch_num+1);
|
||||
DSM_abort_channel_rx(0); // Abort RX operation and receive
|
||||
}
|
||||
}
|
||||
return 1000;
|
||||
case DSM_RX_DATA_CH1:
|
||||
//Packets per second
|
||||
if (millis() - pps_timer >= 1000)
|
||||
{//182pps @11ms, 91pps @22ms
|
||||
pps_timer = millis();
|
||||
if(DSM_rx_type!=0xA2 && DSM_rx_type!=0x01) // if 11ms
|
||||
pps_counter >>=1; // then /2
|
||||
debugln("%d pps", pps_counter);
|
||||
RX_LQI = pps_counter; // max=91pps
|
||||
pps_counter = 0;
|
||||
}
|
||||
//Received something ?
|
||||
rx_status = DSM_RX_check_packet();
|
||||
if(rx_status == 0x02)
|
||||
{ // data received with no errors
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH1:RX");
|
||||
#endif
|
||||
DSM_RX_build_telemetry_packet();
|
||||
rx_data_started = true;
|
||||
pps_counter++;
|
||||
DSM_abort_channel_rx(2); // Abort RX operation, set sop&data&seed&rf using CH2, DSM2/X and receive
|
||||
phase++;
|
||||
return 5000;
|
||||
}
|
||||
else
|
||||
{
|
||||
read_retry++;
|
||||
if(rx_data_started && read_retry>6) // After 6*500=3ms
|
||||
{ // skip to CH2
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH1:Skip to CH2");
|
||||
#endif
|
||||
DSM_abort_channel_rx(2); // Abort RX operation, set sop&data&seed&rf using CH2, DSM2/X and receive
|
||||
phase++;
|
||||
return 4000;
|
||||
}
|
||||
if(rx_data_started && RX_LQI==0)
|
||||
{ // communication lost
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH1:Restart...");
|
||||
#endif
|
||||
phase=DSM_RX_DATA_PREP;
|
||||
return 1000;
|
||||
}
|
||||
if(read_retry>250)
|
||||
{ // move to next RF channel
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH1:Scan");
|
||||
#endif
|
||||
DSM_abort_channel_rx(3); // Abort RX operation, set sop&data&seed&rf using CH2 then CH1, DSM2/X and receive
|
||||
read_retry=0;
|
||||
}
|
||||
else if(rx_status & 0x02)
|
||||
{ // data received with errors
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH1:RX error %02X",rx_status);
|
||||
#endif
|
||||
DSM_abort_channel_rx(0); // Abort RX operation and receive
|
||||
}
|
||||
}
|
||||
return 500;
|
||||
case DSM_RX_DATA_CH2:
|
||||
rx_status = DSM_RX_check_packet();
|
||||
if(rx_status == 0x02)
|
||||
{ // data received with no errors
|
||||
#ifdef DSM_DEBUG_RF
|
||||
debugln("CH2:RX");
|
||||
#endif
|
||||
DSM_RX_build_telemetry_packet();
|
||||
pps_counter++;
|
||||
}
|
||||
#ifdef DSM_DEBUG_RF
|
||||
else
|
||||
debugln("CH2:No RX");
|
||||
#endif
|
||||
DSM_abort_channel_rx(1); // Abort RX operation, set sop&data&seed&rf using CH1, DSM2/X and receive
|
||||
read_retry=0;
|
||||
phase=DSM_RX_DATA_CH1;
|
||||
if(DSM_rx_type==0xA2) //|| DSM_rx_type==0x01 -> not needed for DSM2 since we are ok to listen even if there will be nothing
|
||||
return 15000; //22ms
|
||||
else
|
||||
return 4000; //11ms
|
||||
}
|
||||
return 10000;
|
||||
}
|
||||
|
||||
void DSM_RX_init()
|
||||
{
|
||||
DSM_RX_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet_count=0;
|
||||
phase = DSM_RX_BIND1;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t temp = DSM_RX_EEPROM_OFFSET;
|
||||
debug("ID=");
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
{
|
||||
cyrfmfg_id[i]=eeprom_read_byte((EE_ADDR)temp++);
|
||||
debug(" %02X", cyrfmfg_id[i]);
|
||||
}
|
||||
DSM_rx_type=eeprom_read_byte((EE_ADDR)temp);
|
||||
debugln(", type=%02X", DSM_rx_type);
|
||||
phase = DSM_RX_DATA_PREP;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
501
Multiprotocol/DSM_cyrf6936.ino
Normal file
501
Multiprotocol/DSM_cyrf6936.ino
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(DSM_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
//#define DSM_DEBUG_FWD_PGM
|
||||
|
||||
//#define DSM_GR300
|
||||
|
||||
#define DSM_BIND_CHANNEL 0x0d //13 This can be any odd channel
|
||||
|
||||
//During binding we will send BIND_COUNT/2 packets
|
||||
//One packet each 10msec
|
||||
#define DSM_BIND_COUNT 300
|
||||
|
||||
enum {
|
||||
DSM_BIND_WRITE=0,
|
||||
DSM_BIND_CHECK,
|
||||
DSM_BIND_READ,
|
||||
DSM_CHANSEL,
|
||||
DSM_CH1_WRITE_A,
|
||||
DSM_CH1_CHECK_A,
|
||||
DSM_CH2_WRITE_A,
|
||||
DSM_CH2_CHECK_A,
|
||||
DSM_CH2_READ_A,
|
||||
DSM_CH1_WRITE_B,
|
||||
DSM_CH1_CHECK_B,
|
||||
DSM_CH2_WRITE_B,
|
||||
DSM_CH2_CHECK_B,
|
||||
DSM_CH2_READ_B,
|
||||
};
|
||||
|
||||
//
|
||||
uint8_t ch_map[14];
|
||||
const uint8_t PROGMEM DSM_ch_map_progmem[][14] = {
|
||||
//22+11ms for 3..7 channels
|
||||
{1, 0, 2, 0xff, 0xff, 0xff, 0xff, 1, 0, 2, 0xff, 0xff, 0xff, 0xff}, //3ch - Guess
|
||||
{1, 0, 2, 3, 0xff, 0xff, 0xff, 1, 0, 2, 3, 0xff, 0xff, 0xff}, //4ch - Guess
|
||||
{1, 0, 2, 3, 4, 0xff, 0xff, 1, 0, 2, 3, 4, 0xff, 0xff}, //5ch - Guess
|
||||
{1, 5, 2, 3, 0, 4, 0xff, 1, 5, 2, 3, 0, 4, 0xff}, //6ch - HP6DSM
|
||||
{1, 5, 2, 4, 3, 6, 0, 1, 5, 2, 4, 3, 6, 0 }, //7ch - DX6i
|
||||
//22ms for 8..12 channels
|
||||
{1, 5, 2, 3, 6, 0xff, 0xff, 4, 0, 7, 0xff, 0xff, 0xff, 0xff}, //8ch - DX8/DX7
|
||||
{1, 5, 2, 3, 6, 0xff, 0xff, 4, 0, 7, 8, 0xff, 0xff, 0xff}, //9ch - Guess
|
||||
{1, 5, 2, 3, 6, 0xff, 0xff, 4, 0, 7, 8, 9, 0xff, 0xff}, //10ch - Guess
|
||||
{1, 5, 2, 3, 6, 10, 0xff, 4, 0, 7, 8, 9, 0xff, 0xff}, //11ch - Guess
|
||||
{1, 5, 2, 4, 6, 10, 0xff, 0, 7, 3, 8, 9 , 11 , 0xff}, //12ch - DX18/DX8G2
|
||||
//11ms for 8..11 channels
|
||||
{1, 5, 2, 3, 6, 7, 0xff, 1, 5, 2, 4, 0, 0xff, 0xff}, //8ch - DX7
|
||||
{1, 5, 2, 3, 6, 7, 0xff, 1, 5, 2, 4, 0, 8, 0xff}, //9ch - Guess
|
||||
{1, 5, 2, 3, 4, 8, 9, 1, 5, 2, 3, 0, 7, 6 }, //10ch - DX18
|
||||
{1, 5, 2, 3, 4, 8, 9, 1, 10, 2, 3, 0, 7, 6 }, //11ch - Guess
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DSM_build_bind_packet()
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t sum = 384 - 0x10;//
|
||||
packet[0] = 0xff ^ cyrfmfg_id[0];
|
||||
packet[1] = 0xff ^ cyrfmfg_id[1];
|
||||
packet[2] = 0xff ^ cyrfmfg_id[2];
|
||||
packet[3] = 0xff ^ cyrfmfg_id[3];
|
||||
packet[4] = packet[0];
|
||||
packet[5] = packet[1];
|
||||
packet[6] = packet[2];
|
||||
packet[7] = packet[3];
|
||||
for(i = 0; i < 8; i++)
|
||||
sum += packet[i];
|
||||
packet[8] = sum >> 8;
|
||||
packet[9] = sum & 0xff;
|
||||
packet[10] = 0x01; // ???
|
||||
if(sub_protocol==DSM_AUTO)
|
||||
packet[11] = 12;
|
||||
else
|
||||
packet[11] = num_ch;
|
||||
|
||||
if (sub_protocol==DSM2_1F)
|
||||
packet[12]=num_ch<8?0x01:0x02; // DSM2/1024 1 or 2 packets depending on the number of channels
|
||||
else if(sub_protocol==DSM2_2F)
|
||||
packet[12]=0x12; // DSM2/2048 2 packets
|
||||
else if(sub_protocol==DSMX_1F)
|
||||
#if defined DSM_TELEMETRY
|
||||
packet[12] = 0xb2; // DSMX/2048 2 packets
|
||||
#else
|
||||
packet[12] = num_ch<8? 0xa2 : 0xb2; // DSMX/2048 1 or 2 packets depending on the number of channels
|
||||
#endif
|
||||
else // DSMX_2F && DSM_AUTO
|
||||
packet[12]=0xb2; // DSMX/2048 2 packets
|
||||
|
||||
packet[13] = 0x00; //???
|
||||
for(i = 8; i < 14; i++)
|
||||
sum += packet[i];
|
||||
packet[14] = sum >> 8;
|
||||
packet[15] = sum & 0xff;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_initialize_bind_phase()
|
||||
{
|
||||
CYRF_ConfigRFChannel(DSM_BIND_CHANNEL); //This seems to be random?
|
||||
//64 SDR Mode is configured so only the 8 first values are needed but need to write 16 values...
|
||||
uint8_t code[16];
|
||||
DSM_read_code(code,0,8,8);
|
||||
CYRF_ConfigDataCode(code, 16);
|
||||
DSM_build_bind_packet();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_update_channels()
|
||||
{
|
||||
prev_option=option;
|
||||
num_ch=option & 0x0F; // Remove flags 0x80=max_throw, 0x40=11ms
|
||||
|
||||
if(num_ch<3 || num_ch>12)
|
||||
num_ch=6; // Default to 6 channels if invalid choice...
|
||||
|
||||
// Create channel map based on number of channels and refresh rate
|
||||
uint8_t idx=num_ch-3;
|
||||
if((option & 0x40) && num_ch>7 && num_ch<12)
|
||||
idx+=5; // In 11ms mode change index only for channels 8..11
|
||||
for(uint8_t i=0;i<14;i++)
|
||||
ch_map[i]=pgm_read_byte_near(&DSM_ch_map_progmem[idx][i]);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DSM_build_data_packet(uint8_t upper)
|
||||
{
|
||||
uint8_t bits = 11;
|
||||
|
||||
if(prev_option!=option)
|
||||
DSM_update_channels();
|
||||
|
||||
if (sub_protocol==DSMX_2F || sub_protocol==DSMX_1F )
|
||||
{//DSMX
|
||||
packet[0] = cyrfmfg_id[2];
|
||||
packet[1] = cyrfmfg_id[3];
|
||||
}
|
||||
else
|
||||
{//DSM2
|
||||
packet[0] = (0xff ^ cyrfmfg_id[2]);
|
||||
packet[1] = (0xff ^ cyrfmfg_id[3]);
|
||||
if(sub_protocol==DSM2_1F)
|
||||
bits=10; // Only DSM2_1F is using a resolution of 1024
|
||||
}
|
||||
|
||||
#ifdef DSM_THROTTLE_KILL_CH
|
||||
uint16_t kill_ch=Channel_data[DSM_THROTTLE_KILL_CH-1];
|
||||
#endif
|
||||
for (uint8_t i = 0; i < 7; i++)
|
||||
{
|
||||
uint8_t idx = ch_map[(upper?7:0) + i]; // 1,5,2,3,0,4
|
||||
uint16_t value = 0xffff;
|
||||
if((option&0x40) == 0 && num_ch < 8 && upper)
|
||||
idx=0xff; // in 22ms do not transmit upper channels if <8, is it the right method???
|
||||
if (idx != 0xff)
|
||||
{
|
||||
/* Spektrum own remotes transmit normal values during bind and actually use this (e.g. Nano CP X) to
|
||||
select the transmitter mode (e.g. computer vs non-computer radio), so always send normal output */
|
||||
#ifdef DSM_THROTTLE_KILL_CH
|
||||
if(idx==CH1 && kill_ch<=604)
|
||||
{//Activate throttle kill only if channel is throttle and DSM_THROTTLE_KILL_CH below -50%
|
||||
if(kill_ch<CHANNEL_MIN_100) // restrict val to 0...400
|
||||
kill_ch=0;
|
||||
else
|
||||
kill_ch-=CHANNEL_MIN_100;
|
||||
value=(kill_ch*21)/25; // kill channel -100%->904us ... -50%->1100us *0x150/400
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef DSM_MAX_THROW
|
||||
value=Channel_data[CH_TAER[idx]]; // -100%..+100% => 1024..1976us and -125%..+125% => 904..2096us based on Redcon 6 channel DSM2 RX
|
||||
#else
|
||||
if(option & 0x80)
|
||||
value=Channel_data[CH_TAER[idx]]; // -100%..+100% => 1024..1976us and -125%..+125% => 904..2096us based on Redcon 6 channel DSM2 RX
|
||||
else
|
||||
value=convert_channel_16b_nolimit(CH_TAER[idx],0x156,0x6AA,false); // -100%..+100% => 1100..1900us and -125%..+125% => 1000..2000us based on a DX8 G2 dump
|
||||
#endif
|
||||
if(bits==10) value>>=1;
|
||||
value |= (upper && i==0 ? 0x8000 : 0) | (idx << bits);
|
||||
}
|
||||
packet[i*2+2] = (value >> 8) & 0xff;
|
||||
packet[i*2+3] = (value >> 0) & 0xff;
|
||||
}
|
||||
#ifdef DSM_FWD_PGM
|
||||
if(upper==0 && DSM_SerialRX && (DSM_SerialRX_val[0]&0xF8)==0x70 )
|
||||
{ // Send forward programming data if available
|
||||
for(uint8_t i=0; i<(DSM_SerialRX_val[0]&0x07);i++)
|
||||
{
|
||||
packet[i*2+4]=0x70+i;
|
||||
packet[i*2+5]=DSM_SerialRX_val[i+1];
|
||||
}
|
||||
DSM_SerialRX=false;
|
||||
#ifdef DSM_DEBUG_FWD_PGM
|
||||
debug("FWD=");
|
||||
for(uint8_t i=4; i<16;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) DSM_Check_RX_packet()
|
||||
{
|
||||
uint8_t result=1; // assume good packet
|
||||
|
||||
uint16_t sum = 384 - 0x10;
|
||||
for(uint8_t i = 1; i < 9; i++)
|
||||
{
|
||||
sum += packet_in[i];
|
||||
if(i<5)
|
||||
if(packet_in[i] != (0xff ^ cyrfmfg_id[i-1]))
|
||||
result=0; // bad packet
|
||||
}
|
||||
if( packet_in[9] != (sum>>8) && packet_in[10] != (uint8_t)sum )
|
||||
result=0;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t DSM_callback()
|
||||
{
|
||||
#define DSM_CH1_CH2_DELAY 4010 // Time between write of channel 1 and channel 2
|
||||
#ifdef STM32_BOARD
|
||||
#define DSM_WRITE_DELAY 1600 // Time after write to verify write complete
|
||||
#else
|
||||
#define DSM_WRITE_DELAY 1950 // Time after write to verify write complete
|
||||
#endif
|
||||
#define DSM_READ_DELAY 600 // Time before write to check read phase, and switch channels. Was 400 but 600 seems what the 328p needs to read a packet
|
||||
#if defined DSM_TELEMETRY
|
||||
uint8_t rx_phase;
|
||||
uint8_t len;
|
||||
#endif
|
||||
uint8_t start;
|
||||
|
||||
#ifdef DSM_GR300
|
||||
uint16_t timing=5000+(convert_channel_8b(CH13)*100);
|
||||
debugln("T=%u",timing);
|
||||
#endif
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case DSM_BIND_WRITE:
|
||||
if(bind_counter--==0)
|
||||
#if defined DSM_TELEMETRY
|
||||
phase=DSM_BIND_CHECK; //Check RX answer
|
||||
#else
|
||||
phase=DSM_CHANSEL; //Switch to normal mode
|
||||
#endif
|
||||
CYRF_WriteDataPacket(packet);
|
||||
return 10000;
|
||||
#if defined DSM_TELEMETRY
|
||||
case DSM_BIND_CHECK:
|
||||
//64 SDR Mode is configured so only the 8 first values are needed but we need to write 16 values...
|
||||
CYRF_ConfigDataCode((const uint8_t *)"\x98\x88\x1B\xE4\x30\x79\x03\x84", 16);
|
||||
CYRF_SetTxRxMode(RX_EN); //Receive mode
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //Prepare to receive
|
||||
bind_counter=2*DSM_BIND_COUNT; //Timeout of 4.2s if no packet received
|
||||
phase++; // change from BIND_CHECK to BIND_READ
|
||||
return 2000;
|
||||
case DSM_BIND_READ:
|
||||
//Read data from RX
|
||||
rx_phase = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_phase & 0x03) == 0x02) // RXC=1, RXE=0 then 2nd check is required (debouncing)
|
||||
rx_phase |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_phase & 0x07) == 0x02)
|
||||
{ // data received with no errors
|
||||
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80);// Need to set RXOW before data read
|
||||
if(CYRF_ReadRegister(CYRF_09_RX_COUNT)==10) // Len
|
||||
{
|
||||
CYRF_ReadDataPacketLen(packet_in+1, 10);
|
||||
if(DSM_Check_RX_packet())
|
||||
{
|
||||
debug("Bind");
|
||||
for(uint8_t i=0;i<10;i++)
|
||||
debug(" %02X",packet_in[i+1]);
|
||||
debugln("");
|
||||
packet_in[0]=0x80;
|
||||
packet_in[6]&=0x0F; // It looks like there is a flag 0x40 being added by some receivers
|
||||
if(packet_in[6]>12) packet_in[6]=12;
|
||||
else if(packet_in[6]<3) packet_in[6]=6;
|
||||
telemetry_link=1; // Send received data on serial
|
||||
phase++;
|
||||
return 2000;
|
||||
}
|
||||
}
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
CYRF_SetTxRxMode(RX_EN); // Force end state read
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX operation
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); // Prepare to receive
|
||||
}
|
||||
else
|
||||
if((rx_phase & 0x02) != 0x02)
|
||||
{ // data received with errors
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
CYRF_SetTxRxMode(RX_EN); // Force end state read
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX operation
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x83); // Prepare to receive
|
||||
}
|
||||
if( --bind_counter == 0 )
|
||||
{ // Exit if no answer has been received for some time
|
||||
phase++; // DSM_CHANSEL
|
||||
return 7000 ;
|
||||
}
|
||||
return 7000;
|
||||
#endif
|
||||
case DSM_CHANSEL:
|
||||
BIND_DONE;
|
||||
DSM_cyrf_configdata();
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
hopping_frequency_no = 0;
|
||||
phase = DSM_CH1_WRITE_A; // in fact phase++
|
||||
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F);
|
||||
return 10000;
|
||||
case DSM_CH1_WRITE_A:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(11000); // Always request 11ms spacing even if we don't use half of it in 22ms mode
|
||||
#endif
|
||||
CYRF_SetPower(0x28); //Keep transmit power in sync
|
||||
case DSM_CH1_WRITE_B:
|
||||
DSM_build_data_packet(phase == DSM_CH1_WRITE_B); // build lower or upper channels
|
||||
case DSM_CH2_WRITE_A:
|
||||
case DSM_CH2_WRITE_B:
|
||||
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS); // clear IRQ flags
|
||||
CYRF_WriteDataPacket(packet);
|
||||
#if 0
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
debug(" %02X", packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
phase++; // change from WRITE to CHECK mode
|
||||
return DSM_WRITE_DELAY;
|
||||
case DSM_CH1_CHECK_A:
|
||||
case DSM_CH1_CHECK_B:
|
||||
case DSM_CH2_CHECK_A:
|
||||
case DSM_CH2_CHECK_B:
|
||||
start=(uint8_t)micros();
|
||||
while ((uint8_t)((uint8_t)micros()-(uint8_t)start) < 100) // Wait max 100µs, max I've seen is 50µs
|
||||
if((CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80) == 0x00)
|
||||
break;
|
||||
|
||||
if(phase==DSM_CH1_CHECK_A || phase==DSM_CH1_CHECK_B)
|
||||
{
|
||||
#if defined DSM_TELEMETRY
|
||||
// reset cyrf6936 if freezed after switching from TX to RX
|
||||
if (((CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x22) == 0x20) || (CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80))
|
||||
{
|
||||
CYRF_Reset();
|
||||
DSM_cyrf_config();
|
||||
DSM_cyrf_configdata();
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
}
|
||||
#endif
|
||||
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F || sub_protocol==DSMX_1F);
|
||||
phase++; // change from CH1_CHECK to CH2_WRITE
|
||||
return DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY;
|
||||
}
|
||||
#if defined DSM_TELEMETRY
|
||||
phase++; // change from CH2_CHECK to CH2_READ
|
||||
CYRF_SetTxRxMode(RX_EN); //Receive mode
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
|
||||
#ifdef DSM_GR300
|
||||
if(num_ch==3)
|
||||
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
|
||||
#endif
|
||||
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
|
||||
case DSM_CH2_READ_A:
|
||||
case DSM_CH2_READ_B:
|
||||
//Read telemetry
|
||||
rx_phase = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_phase & 0x03) == 0x02) // RXC=1, RXE=0 then 2nd check is required (debouncing)
|
||||
rx_phase |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_phase & 0x07) == 0x02)
|
||||
{ // good data (complete with no errors)
|
||||
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80); // need to set RXOW before data read
|
||||
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
if(len>TELEMETRY_BUFFER_SIZE-2)
|
||||
len=TELEMETRY_BUFFER_SIZE-2;
|
||||
CYRF_ReadDataPacketLen(packet_in+1, len);
|
||||
#ifdef DSM_DEBUG_FWD_PGM
|
||||
//debug(" %02X", packet_in[1]);
|
||||
if(packet_in[1]==9)
|
||||
{
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
debug(" %02X", packet_in[i+1]);
|
||||
debugln("");
|
||||
}
|
||||
#endif
|
||||
packet_in[0]=CYRF_ReadRegister(CYRF_13_RSSI)&0x1F;// store RSSI of the received telemetry signal
|
||||
telemetry_link=1;
|
||||
}
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
|
||||
if (phase == DSM_CH2_READ_A && (sub_protocol==DSM2_1F || sub_protocol==DSMX_1F) && num_ch < 8) // 22ms mode
|
||||
{
|
||||
CYRF_SetTxRxMode(RX_EN); // Force end state read
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Clear abort RX operation
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
|
||||
phase = DSM_CH2_READ_B;
|
||||
#ifdef DSM_GR300
|
||||
if(num_ch==3)
|
||||
return timing;
|
||||
#endif
|
||||
return 11000;
|
||||
}
|
||||
if (phase == DSM_CH2_READ_A)
|
||||
phase = DSM_CH1_WRITE_B; //Transmit upper
|
||||
else
|
||||
phase = DSM_CH1_WRITE_A; //Transmit lower
|
||||
CYRF_SetTxRxMode(TX_EN); //TX mode
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); //Clear abort RX operation
|
||||
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F);
|
||||
return DSM_READ_DELAY;
|
||||
#else
|
||||
// No telemetry
|
||||
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F);
|
||||
if (phase == DSM_CH2_CHECK_A)
|
||||
{
|
||||
if(num_ch > 7 || sub_protocol==DSM2_2F || sub_protocol==DSMX_2F)
|
||||
phase = DSM_CH1_WRITE_B; //11ms mode or upper to transmit change from CH2_CHECK_A to CH1_WRITE_A
|
||||
else
|
||||
{ //Normal mode 22ms
|
||||
phase = DSM_CH1_WRITE_A; // change from CH2_CHECK_A to CH1_WRITE_A (ie no upper)
|
||||
#ifdef DSM_GR300
|
||||
if(num_ch==3)
|
||||
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
|
||||
#endif
|
||||
return 22000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
|
||||
}
|
||||
}
|
||||
else
|
||||
phase = DSM_CH1_WRITE_A; // change from CH2_CHECK_B to CH1_WRITE_A (upper already transmitted so transmit lower)
|
||||
#ifdef DSM_GR300
|
||||
if(num_ch==3)
|
||||
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY ;
|
||||
#endif
|
||||
return 11000 - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSM_init()
|
||||
{
|
||||
CYRF_GetMfgData(cyrfmfg_id);
|
||||
//Model match
|
||||
cyrfmfg_id[3]^=RX_num;
|
||||
//Calc sop_col
|
||||
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
|
||||
//Fix for OrangeRX using wrong DSM_pncodes by preventing access to "Col 8"
|
||||
if(sop_col==0)
|
||||
{
|
||||
cyrfmfg_id[rx_tx_addr[0]%3]^=0x01; //Change a bit so sop_col will be different from 0
|
||||
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
|
||||
}
|
||||
//Calc CRC seed
|
||||
seed = (cyrfmfg_id[0] << 8) + cyrfmfg_id[1];
|
||||
//Hopping frequencies
|
||||
if (sub_protocol == DSMX_2F || sub_protocol == DSMX_1F)
|
||||
DSM_calc_dsmx_channel();
|
||||
else
|
||||
{
|
||||
uint8_t tmpch[10];
|
||||
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75);
|
||||
//
|
||||
uint8_t idx = random(0xfefefefe) % 10;
|
||||
hopping_frequency[0] = tmpch[idx];
|
||||
while(1)
|
||||
{
|
||||
idx = random(0xfefefefe) % 10;
|
||||
if (tmpch[idx] != hopping_frequency[0])
|
||||
break;
|
||||
}
|
||||
hopping_frequency[1] = tmpch[idx];
|
||||
}
|
||||
//
|
||||
DSM_cyrf_config();
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
//
|
||||
DSM_update_channels();
|
||||
//
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
DSM_initialize_bind_phase();
|
||||
phase = DSM_BIND_WRITE;
|
||||
bind_counter=DSM_BIND_COUNT;
|
||||
}
|
||||
else
|
||||
phase = DSM_CHANSEL;//
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,20 +17,15 @@
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
#define DEVO_NUM_CHANNELS 8
|
||||
|
||||
//For Debug
|
||||
//#define NO_SCRAMBLE
|
||||
#define PKTS_PER_CHANNEL 4
|
||||
#define DEVO_BIND_COUNT 0x1388
|
||||
//#define TELEMETRY_ENABLE 0x30
|
||||
#define NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
|
||||
//
|
||||
//#define TELEM_ON 0
|
||||
//#define TELEM_OFF 1
|
||||
|
||||
enum Devo_PhaseState
|
||||
{
|
||||
#define DEVO_PKTS_PER_CHANNEL 4
|
||||
#define DEVO_BIND_COUNT 0x1388
|
||||
|
||||
#define DEVO_NUM_WAIT_LOOPS (100 / 5) //each loop is ~5us. Do not wait more than 100us
|
||||
|
||||
enum {
|
||||
DEVO_BIND,
|
||||
DEVO_BIND_SENDCH,
|
||||
DEVO_BOUND,
|
||||
@@ -46,41 +41,38 @@ enum Devo_PhaseState
|
||||
DEVO_BOUND_10,
|
||||
};
|
||||
|
||||
const uint8_t sopcodes[][8] = {
|
||||
/* Note these are in order transmitted (LSB 1st) */
|
||||
/* 0 */ {0x3C,0x37,0xCC,0x91,0xE2,0xF8,0xCC,0x91}, //0x91CCF8E291CC373C
|
||||
/* 1 */ {0x9B,0xC5,0xA1,0x0F,0xAD,0x39,0xA2,0x0F}, //0x0FA239AD0FA1C59B
|
||||
/* 2 */ {0xEF,0x64,0xB0,0x2A,0xD2,0x8F,0xB1,0x2A}, //0x2AB18FD22AB064EF
|
||||
/* 3 */ {0x66,0xCD,0x7C,0x50,0xDD,0x26,0x7C,0x50}, //0x507C26DD507CCD66
|
||||
/* 4 */ {0x5C,0xE1,0xF6,0x44,0xAD,0x16,0xF6,0x44}, //0x44F616AD44F6E15C
|
||||
/* 5 */ {0x5A,0xCC,0xAE,0x46,0xB6,0x31,0xAE,0x46}, //0x46AE31B646AECC5A
|
||||
/* 6 */ {0xA1,0x78,0xDC,0x3C,0x9E,0x82,0xDC,0x3C}, //0x3CDC829E3CDC78A1
|
||||
/* 7 */ {0xB9,0x8E,0x19,0x74,0x6F,0x65,0x18,0x74}, //0x7418656F74198EB9
|
||||
/* 8 */ {0xDF,0xB1,0xC0,0x49,0x62,0xDF,0xC1,0x49}, //0x49C1DF6249C0B1DF
|
||||
/* 9 */ {0x97,0xE5,0x14,0x72,0x7F,0x1A,0x14,0x72}, //0x72141A7F7214E597
|
||||
};
|
||||
|
||||
uint8_t txState;
|
||||
uint8_t pkt_num;
|
||||
uint8_t ch_idx;
|
||||
uint8_t use_fixed_id;
|
||||
uint8_t failsafe_pkt;
|
||||
|
||||
void scramble_pkt()
|
||||
static void __attribute__((unused)) DEVO_scramble_pkt()
|
||||
{
|
||||
#ifdef NO_SCRAMBLE
|
||||
return;
|
||||
#else
|
||||
uint8_t i;
|
||||
for(i = 0; i < 15; i++)
|
||||
for(uint8_t i = 0; i < 15; i++)
|
||||
packet[i + 1] ^= cyrfmfg_id[i % 4];
|
||||
#endif
|
||||
}
|
||||
|
||||
void add_pkt_suffix()
|
||||
static void __attribute__((unused)) DEVO_add_pkt_suffix()
|
||||
{
|
||||
uint8_t bind_state;
|
||||
if (use_fixed_id)
|
||||
#ifdef ENABLE_PPM
|
||||
if(mode_select && option==0 && IS_BIND_DONE) //PPM mode and option not already set and bind is finished
|
||||
{
|
||||
BIND_SET_INPUT;
|
||||
BIND_SET_PULLUP; // set pullup
|
||||
if(IS_BIND_BUTTON_on)
|
||||
{
|
||||
eeprom_write_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num),0x01); // Set fixed id mode for the current model
|
||||
option=1;
|
||||
}
|
||||
BIND_SET_OUTPUT;
|
||||
}
|
||||
#endif //ENABLE_PPM
|
||||
if(prev_option!=option && IS_BIND_DONE)
|
||||
{
|
||||
MProtocol_id = RX_num + MProtocol_id_master;
|
||||
bind_counter=DEVO_BIND_COUNT;
|
||||
}
|
||||
if (option)
|
||||
{
|
||||
if (bind_counter > 0)
|
||||
bind_state = 0xc0;
|
||||
@@ -89,36 +81,46 @@ void add_pkt_suffix()
|
||||
}
|
||||
else
|
||||
bind_state = 0x00;
|
||||
packet[10] = bind_state | (PKTS_PER_CHANNEL - pkt_num - 1);
|
||||
packet[10] = bind_state | (DEVO_PKTS_PER_CHANNEL - packet_count - 1);
|
||||
packet[11] = *(hopping_frequency_ptr + 1);
|
||||
packet[12] = *(hopping_frequency_ptr + 2);
|
||||
packet[13] = fixed_id & 0xff;
|
||||
packet[14] = (fixed_id >> 8) & 0xff;
|
||||
packet[15] = (fixed_id >> 16) & 0xff;
|
||||
packet[13] = MProtocol_id & 0xff;
|
||||
packet[14] = (MProtocol_id >> 8) & 0xff;
|
||||
packet[15] = (MProtocol_id >> 16) & 0xff;
|
||||
}
|
||||
|
||||
void build_beacon_pkt(uint8_t upper)
|
||||
static void __attribute__((unused)) DEVO_build_beacon_pkt(uint8_t upper)
|
||||
{
|
||||
packet[0] = ((DEVO_NUM_CHANNELS << 4) | 0x07);
|
||||
// uint8_t enable = 0;
|
||||
uint8_t max = 8;
|
||||
// int offset = 0;
|
||||
packet[0] = (num_ch << 4) | 0x07;
|
||||
uint8_t max = 8, offset = 0, enable = 0;
|
||||
if (upper)
|
||||
{
|
||||
packet[0] += 1;
|
||||
max = 4;
|
||||
// offset = 8;
|
||||
offset = 8;
|
||||
}
|
||||
for(uint8_t i = 0; i < max; i++)
|
||||
{
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
uint16_t failsafe=Failsafe_data[CH_EATR[i+offset]];
|
||||
if(i + offset < num_ch && failsafe!=FAILSAFE_CHANNEL_HOLD && IS_FAILSAFE_VALUES_on)
|
||||
{
|
||||
enable |= 0x80 >> i;
|
||||
packet[i+1] = ((failsafe*25)>>8)-100;
|
||||
}
|
||||
else
|
||||
#else
|
||||
(void)offset;
|
||||
#endif
|
||||
packet[i+1] = 0;
|
||||
// packet[9] = enable;
|
||||
packet[9] = 0;
|
||||
add_pkt_suffix();
|
||||
}
|
||||
packet[9] = enable;
|
||||
DEVO_add_pkt_suffix();
|
||||
}
|
||||
|
||||
void build_bind_pkt()
|
||||
static void __attribute__((unused)) DEVO_build_bind_pkt()
|
||||
{
|
||||
packet[0] = (DEVO_NUM_CHANNELS << 4) | 0x0a;
|
||||
packet[0] = (num_ch << 4) | 0x0a;
|
||||
packet[1] = bind_counter & 0xff;
|
||||
packet[2] = (bind_counter >> 8);
|
||||
packet[3] = *hopping_frequency_ptr;
|
||||
@@ -128,7 +130,7 @@ void build_bind_pkt()
|
||||
packet[7] = cyrfmfg_id[1];
|
||||
packet[8] = cyrfmfg_id[2];
|
||||
packet[9] = cyrfmfg_id[3];
|
||||
add_pkt_suffix();
|
||||
DEVO_add_pkt_suffix();
|
||||
//The fixed-id portion is scrambled in the bind packet
|
||||
//I assume it is ignored
|
||||
packet[13] ^= cyrfmfg_id[0];
|
||||
@@ -136,16 +138,15 @@ void build_bind_pkt()
|
||||
packet[15] ^= cyrfmfg_id[2];
|
||||
}
|
||||
|
||||
void build_data_pkt()
|
||||
static void __attribute__((unused)) DEVO_build_data_pkt()
|
||||
{
|
||||
uint8_t i;
|
||||
packet[0] = (DEVO_NUM_CHANNELS << 4) | (0x0b + ch_idx);
|
||||
static uint8_t ch_idx=0;
|
||||
|
||||
packet[0] = (num_ch << 4) | (0x0b + ch_idx);
|
||||
uint8_t sign = 0x0b;
|
||||
for (i = 0; i < 4; i++)
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
{
|
||||
//
|
||||
int16_t value= map(Servo_data[ch_idx * 4 + i],PPM_MIN,PPM_MAX,-1600,1600);//range -1600...+1600
|
||||
//s32 value = (s32)Channels[ch_idx * 4 + i] * 0x640 / CHAN_MAX_VALUE;//10000
|
||||
int16_t value=convert_channel_16b_nolimit(CH_EATR[ch_idx * 4 + i],-1600,1600,false);//range -1600..+1600
|
||||
if(value < 0)
|
||||
{
|
||||
value = -value;
|
||||
@@ -155,13 +156,158 @@ void build_data_pkt()
|
||||
packet[2 * i + 2] = (value >> 8) & 0xff;
|
||||
}
|
||||
packet[9] = sign;
|
||||
ch_idx = ch_idx + 1;
|
||||
if (ch_idx * 4 >= DEVO_NUM_CHANNELS)
|
||||
ch_idx++;
|
||||
if (ch_idx * 4 >= num_ch)
|
||||
ch_idx = 0;
|
||||
add_pkt_suffix();
|
||||
DEVO_add_pkt_suffix();
|
||||
}
|
||||
|
||||
void cyrf_set_bound_sop_code()
|
||||
#if defined DEVO_HUB_TELEMETRY
|
||||
static uint32_t __attribute__((unused)) DEVO_text_to_int(uint8_t *ptr, uint8_t len)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
for(uint8_t i = 0; i < len; i++)
|
||||
value = value * 10 + (ptr[i] - '0');
|
||||
return value;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DEVO_float_to_ints(uint8_t *ptr, uint16_t *value, uint16_t *decimal)
|
||||
{
|
||||
bool seen_decimal = false;
|
||||
*value = 0;
|
||||
*decimal = 0;
|
||||
for(uint8_t i = 0; i < 7; i++)
|
||||
{
|
||||
if(ptr[i] == '.')
|
||||
{
|
||||
seen_decimal = true;
|
||||
continue;
|
||||
}
|
||||
if(ptr[i] < '0' || ptr[i] > '9')
|
||||
{
|
||||
if(*value != 0 || seen_decimal)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(seen_decimal)
|
||||
*decimal = *decimal * 10 + (ptr[i] - '0');
|
||||
else
|
||||
*value = *value * 10 + (ptr[i] - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) DEVO_parse_telemetry_packet()
|
||||
{ // Telemetry packets every 2.4ms
|
||||
DEVO_scramble_pkt(); //This will unscramble the packet
|
||||
|
||||
debugln("RX");
|
||||
if ((((uint32_t)packet[15] << 16) | ((uint32_t)packet[14] << 8) | packet[13]) != (MProtocol_id & 0x00ffffff))
|
||||
return; // ID does not match
|
||||
|
||||
if((telemetry_link & 3) != 0)
|
||||
{
|
||||
debugln("S%d",telemetry_link);
|
||||
return; // Previous telemetry not sent yet...
|
||||
}
|
||||
|
||||
//Debug telem RX
|
||||
//for(uint8_t i=0;i<12;i++)
|
||||
// debug("%02X ",packet[i]);
|
||||
//debugln("");
|
||||
|
||||
#if defined HUB_TELEMETRY
|
||||
//Telemetry https://github.com/DeviationTX/deviation/blob/5efb6a28bea697af9a61b5a0ed2528cc8d203f90/src/protocol/devo_cyrf6936.c#L232
|
||||
uint16_t val, dec;
|
||||
switch(packet[0])
|
||||
{
|
||||
case 0x30: // Volt and RPM packet
|
||||
//RSSI and voltage
|
||||
TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI) & 0x1F;
|
||||
TX_RSSI = (TX_RSSI << 1) + TX_RSSI;
|
||||
RX_RSSI = TX_RSSI;
|
||||
telemetry_link |= 1;
|
||||
v_lipo1 = packet[1] << 1;
|
||||
v_lipo2 = packet[3] << 1;
|
||||
//packet[5] = 127; // 12.7V
|
||||
if(packet[5] != 0)
|
||||
{
|
||||
val = (packet[5]*11)/21; // OpenTX strange transformation??
|
||||
dec = val;
|
||||
val /= 10;
|
||||
dec -= val*10;
|
||||
frsky_send_user_frame(0x3A, val, 0x00); // volt3
|
||||
frsky_send_user_frame(0x3B, dec, 0x00); // volt3
|
||||
}
|
||||
val = packet[7] * 120; // change to RPM
|
||||
frsky_send_user_frame(0x03, val, val>>8); // RPM
|
||||
break;
|
||||
case 0x31: // Temperature packet
|
||||
//memcpy(&packet[1],"\x29\x2A\x2B\x00\x00\x00\x00\x00\x00\x00\x00\x00",12); // 21°, 22°, 23°
|
||||
for(uint8_t i=0; i<2;i++)
|
||||
if(packet[i+1]!=0xff)
|
||||
{
|
||||
val = packet[i+1];
|
||||
val -= 20;
|
||||
frsky_send_user_frame(0x02 + i*3, val, val>>8); // temp 1 & 2
|
||||
}
|
||||
break;
|
||||
// GPS Data
|
||||
case 0x32: // Longitude
|
||||
//memcpy(&packet[1],"\x30\x33\x30\x32\x30\x2e\x38\x32\x37\x30\x45\xfb",12); // 030°20.8270E
|
||||
val = DEVO_text_to_int(&packet[1], 3)*100 + DEVO_text_to_int(&packet[4], 2); // dddmm
|
||||
frsky_send_user_frame(0x12 , val, val>>8);
|
||||
val = DEVO_text_to_int(&packet[7], 4); // .mmmm
|
||||
frsky_send_user_frame(0x12+8, val, val>>8);
|
||||
frsky_send_user_frame(0x1A+8, packet[11], 0x00); // 'E'/'W'
|
||||
break;
|
||||
case 0x33: // Latitude
|
||||
//memcpy(&packet[1],"\x35\x39\x35\x34\x2e\x37\x37\x37\x36\x4e\x07\x00",12); // 59°54.776N
|
||||
val = DEVO_text_to_int(&packet[1], 2)*100 + DEVO_text_to_int(&packet[3], 2); // ddmm
|
||||
frsky_send_user_frame(0x13 , val, val>>8);
|
||||
val = DEVO_text_to_int(&packet[6], 4); // .mmmm
|
||||
frsky_send_user_frame(0x13+8, val, val>>8);
|
||||
frsky_send_user_frame(0x1B+8, packet[10], 0x00); // 'N'/'S'
|
||||
break;
|
||||
case 0x34: // Altitude
|
||||
//memcpy(&packet[1],"\x31\x32\x2e\x38\x00\x00\x00\x4d\x4d\x4e\x45\xfb",12); // 12.8 MMNE
|
||||
DEVO_float_to_ints(&packet[1], &val, &dec);
|
||||
frsky_send_user_frame(0x10, val, val>>8);
|
||||
frsky_send_user_frame(0x21, dec, dec>>8);
|
||||
break;
|
||||
case 0x35: // Speed
|
||||
//memcpy(&packet[1],"\x00\x00\x00\x00\x00\x00\x30\x2e\x30\x30\x00\x00",12); // 0.0
|
||||
DEVO_float_to_ints(&packet[1], &val, &dec);
|
||||
frsky_send_user_frame(0x11 , val, val>>8);
|
||||
frsky_send_user_frame(0x11+8, dec, dec>>8);
|
||||
break;
|
||||
case 0x36: // Time
|
||||
//memcpy(&packet[1],"\x31\x38\x32\x35\x35\x32\x31\x35\x31\x30\x31\x32",12); // 2012-10-15 18:25:52 (UTC)
|
||||
if(packet[1]!=0)
|
||||
{
|
||||
frsky_send_user_frame(0x15, DEVO_text_to_int(&packet[9], 2), DEVO_text_to_int(&packet[7], 2)); // month, day
|
||||
frsky_send_user_frame(0x16, DEVO_text_to_int(&packet[11], 2)+24, 0x00); // year
|
||||
frsky_send_user_frame(0x17, DEVO_text_to_int(&packet[1], 2), DEVO_text_to_int(&packet[3], 2)); // hour, min
|
||||
frsky_send_user_frame(0x18, DEVO_text_to_int(&packet[5], 2), 0x00); // second
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if(packet[0] == 0x30)
|
||||
{
|
||||
TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI) & 0x1F;
|
||||
TX_RSSI = (TX_RSSI << 1) + TX_RSSI;
|
||||
RX_RSSI = TX_RSSI;
|
||||
telemetry_link |= 1;
|
||||
v_lipo1 = packet[1] << 1;
|
||||
v_lipo2 = packet[3] << 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) DEVO_cyrf_set_bound_sop_code()
|
||||
{
|
||||
/* crc == 0 isn't allowed, so use 1 if the math results in 0 */
|
||||
uint8_t crc = (cyrfmfg_id[0] + (cyrfmfg_id[1] >> 6) + cyrfmfg_id[2]);
|
||||
@@ -170,68 +316,62 @@ void cyrf_set_bound_sop_code()
|
||||
uint8_t sopidx = (0xff &((cyrfmfg_id[0] << 2) + cyrfmfg_id[1] + cyrfmfg_id[2])) % 10;
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
CYRF_ConfigCRCSeed((crc << 8) + crc);
|
||||
CYRF_ConfigSOPCode(sopcodes[sopidx]);
|
||||
CYRF_PROGMEM_ConfigSOPCode(DEVO_j6pro_sopcodes[sopidx]);
|
||||
CYRF_SetPower(0x08);
|
||||
}
|
||||
|
||||
void cyrf_init()
|
||||
const uint8_t PROGMEM DEVO_init_vals[][2] = {
|
||||
{ CYRF_1D_MODE_OVERRIDE, 0x38 },
|
||||
{ CYRF_03_TX_CFG, 0x08 },
|
||||
{ CYRF_06_RX_CFG, 0x4A },
|
||||
{ CYRF_0B_PWR_CTRL, 0x00 },
|
||||
{ CYRF_10_FRAMING_CFG, 0xA4 },
|
||||
{ CYRF_11_DATA32_THOLD, 0x05 },
|
||||
{ CYRF_12_DATA64_THOLD, 0x0E },
|
||||
{ CYRF_1B_TX_OFFSET_LSB, 0x55 },
|
||||
{ CYRF_1C_TX_OFFSET_MSB, 0x05 },
|
||||
{ CYRF_32_AUTO_CAL_TIME, 0x3C },
|
||||
{ CYRF_35_AUTOCAL_OFFSET, 0x14 },
|
||||
{ CYRF_39_ANALOG_CTRL, 0x01 },
|
||||
{ CYRF_1E_RX_OVERRIDE, 0x10 },
|
||||
{ CYRF_1F_TX_OVERRIDE, 0x00 },
|
||||
{ CYRF_01_TX_LENGTH, 0x10 },
|
||||
{ CYRF_0F_XACT_CFG, 0x10 },
|
||||
{ CYRF_27_CLK_OVERRIDE, 0x02 },
|
||||
{ CYRF_28_CLK_EN, 0x02 },
|
||||
{ CYRF_0F_XACT_CFG, 0x28 }
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) DEVO_cyrf_init()
|
||||
{
|
||||
/* Initialise CYRF chip */
|
||||
CYRF_WriteRegister(CYRF_1D_MODE_OVERRIDE, 0x39);
|
||||
CYRF_SetPower(0x08);
|
||||
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4A);
|
||||
CYRF_WriteRegister(CYRF_0B_PWR_CTRL, 0x00);
|
||||
CYRF_WriteRegister(CYRF_0D_IO_CFG, 0x04);
|
||||
CYRF_WriteRegister(CYRF_0E_GPIO_CTRL, 0x20);
|
||||
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xA4);
|
||||
CYRF_WriteRegister(CYRF_11_DATA32_THOLD, 0x05);
|
||||
CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0E);
|
||||
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
|
||||
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
|
||||
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C);
|
||||
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
|
||||
CYRF_WriteRegister(CYRF_39_ANALOG_CTRL, 0x01);
|
||||
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x10);
|
||||
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
|
||||
CYRF_WriteRegister(CYRF_01_TX_LENGTH, 0x10);
|
||||
CYRF_WriteRegister(CYRF_0C_XTAL_CTRL, 0xC0);
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x10);
|
||||
CYRF_WriteRegister(CYRF_27_CLK_OVERRIDE, 0x02);
|
||||
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x28);
|
||||
for(uint8_t i = 0; i < sizeof(DEVO_init_vals) / 2; i++)
|
||||
CYRF_WriteRegister(pgm_read_byte( &DEVO_init_vals[i][0]), pgm_read_byte( &DEVO_init_vals[i][1]) );
|
||||
}
|
||||
|
||||
void set_radio_channels()
|
||||
static void __attribute__((unused)) DEVO_set_radio_channels()
|
||||
{
|
||||
//int i;
|
||||
CYRF_FindBestChannels(hopping_frequency, 3, 4, 4, 80);
|
||||
//printf("Radio Channels:");
|
||||
// for (i = 0; i < 3; i++) {
|
||||
// printf(" %02x", radio_ch[i]);
|
||||
|
||||
//Serial.print(radio_ch[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
//Makes code a little easier to duplicate these here
|
||||
hopping_frequency[3] = hopping_frequency[0];
|
||||
hopping_frequency[4] = hopping_frequency[1];
|
||||
}
|
||||
|
||||
void DEVO_BuildPacket()
|
||||
static void __attribute__((unused)) DEVO_BuildPacket()
|
||||
{
|
||||
static uint8_t failsafe_pkt=0;
|
||||
switch(phase)
|
||||
{
|
||||
case DEVO_BIND:
|
||||
if(bind_counter>0)
|
||||
if(bind_counter)
|
||||
bind_counter--;
|
||||
build_bind_pkt();
|
||||
DEVO_build_bind_pkt();
|
||||
phase = DEVO_BIND_SENDCH;
|
||||
break;
|
||||
case DEVO_BIND_SENDCH:
|
||||
if(bind_counter>0)
|
||||
if(bind_counter)
|
||||
bind_counter--;
|
||||
build_data_pkt();
|
||||
scramble_pkt();
|
||||
DEVO_build_data_pkt();
|
||||
DEVO_scramble_pkt();
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
phase = DEVO_BOUND;
|
||||
@@ -250,10 +390,10 @@ void DEVO_BuildPacket()
|
||||
case DEVO_BOUND_7:
|
||||
case DEVO_BOUND_8:
|
||||
case DEVO_BOUND_9:
|
||||
build_data_pkt();
|
||||
scramble_pkt();
|
||||
DEVO_build_data_pkt();
|
||||
DEVO_scramble_pkt();
|
||||
phase++;
|
||||
if (bind_counter > 0)
|
||||
if (bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
@@ -261,21 +401,101 @@ void DEVO_BuildPacket()
|
||||
}
|
||||
break;
|
||||
case DEVO_BOUND_10:
|
||||
build_beacon_pkt(DEVO_NUM_CHANNELS > 8 ? failsafe_pkt : 0);
|
||||
DEVO_build_beacon_pkt(num_ch > 8 ? failsafe_pkt : 0);
|
||||
failsafe_pkt = failsafe_pkt ? 0 : 1;
|
||||
scramble_pkt();
|
||||
DEVO_scramble_pkt();
|
||||
phase = DEVO_BOUND_1;
|
||||
break;
|
||||
}
|
||||
pkt_num++;
|
||||
if(pkt_num == PKTS_PER_CHANNEL)
|
||||
pkt_num = 0;
|
||||
packet_count++;
|
||||
if(packet_count == DEVO_PKTS_PER_CHANNEL)
|
||||
packet_count = 0;
|
||||
}
|
||||
|
||||
uint16_t devo_callback()
|
||||
uint16_t DEVO_callback()
|
||||
{
|
||||
static uint8_t txState=0;
|
||||
|
||||
#if defined DEVO_HUB_TELEMETRY
|
||||
int delay;
|
||||
|
||||
if (txState == 0)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(2400);
|
||||
#endif
|
||||
DEVO_BuildPacket();
|
||||
CYRF_WriteDataPacket(packet);
|
||||
txState = 1;
|
||||
return 900;
|
||||
}
|
||||
if (txState == 1)
|
||||
{
|
||||
int i = 0;
|
||||
uint8_t reg;
|
||||
while (! ((reg = CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS)) & 0x02))
|
||||
{
|
||||
if (++i >= DEVO_NUM_WAIT_LOOPS)
|
||||
break;
|
||||
}
|
||||
if (((reg & 0x22) == 0x20) || (CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80))
|
||||
{
|
||||
CYRF_Reset();
|
||||
DEVO_cyrf_init();
|
||||
DEVO_cyrf_set_bound_sop_code();
|
||||
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
|
||||
//printf("Rst CYRF\n");
|
||||
delay = 1500;
|
||||
txState = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (phase == DEVO_BOUND)
|
||||
{
|
||||
/* exit binding state */
|
||||
phase = DEVO_BOUND_3;
|
||||
DEVO_cyrf_set_bound_sop_code();
|
||||
}
|
||||
if((packet_count != 0) && (bind_counter == 0))
|
||||
{
|
||||
CYRF_SetTxRxMode(RX_EN); //Receive mode
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
|
||||
txState = 2;
|
||||
return 1300;
|
||||
}
|
||||
}
|
||||
if(packet_count == 0)
|
||||
{
|
||||
CYRF_SetPower(0x08); //Keep tx power updated
|
||||
hopping_frequency_ptr = hopping_frequency_ptr == &hopping_frequency[2] ? hopping_frequency : hopping_frequency_ptr + 1;
|
||||
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
|
||||
}
|
||||
delay = 1500;
|
||||
}
|
||||
if(txState == 2)
|
||||
{
|
||||
uint8_t rx_state = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx_state & 0x03) == 0x02)
|
||||
{ // RXC=1, RXE=0 then 2nd check is required (debouncing)
|
||||
rx_state |= CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
}
|
||||
if((rx_state & 0x07) == 0x02)
|
||||
{ // good data (complete with no errors)
|
||||
CYRF_WriteRegister(CYRF_07_RX_IRQ_STATUS, 0x80); // need to set RXOW before data read
|
||||
CYRF_ReadDataPacketLen(packet, CYRF_ReadRegister(CYRF_09_RX_COUNT));
|
||||
DEVO_parse_telemetry_packet();
|
||||
}
|
||||
CYRF_SetTxRxMode(TX_EN); //Write mode
|
||||
delay = 200;
|
||||
}
|
||||
txState = 0;
|
||||
return delay;
|
||||
#else
|
||||
if (txState == 0)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(2400);
|
||||
#endif
|
||||
txState = 1;
|
||||
DEVO_BuildPacket();
|
||||
CYRF_WriteDataPacket(packet);
|
||||
@@ -284,108 +504,88 @@ uint16_t devo_callback()
|
||||
txState = 0;
|
||||
uint8_t i = 0;
|
||||
while (! (CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS) & 0x02))
|
||||
if(++i > NUM_WAIT_LOOPS)
|
||||
if(++i > DEVO_NUM_WAIT_LOOPS)
|
||||
return 1200;
|
||||
if (phase == DEVO_BOUND)
|
||||
{
|
||||
/* exit binding state */
|
||||
phase = DEVO_BOUND_3;
|
||||
cyrf_set_bound_sop_code();
|
||||
DEVO_cyrf_set_bound_sop_code();
|
||||
}
|
||||
if(pkt_num == 0)
|
||||
if(packet_count == 0)
|
||||
{
|
||||
//Keep tx power updated
|
||||
CYRF_SetPower(0x08);
|
||||
CYRF_SetPower(0x08); //Keep tx power updated
|
||||
hopping_frequency_ptr = hopping_frequency_ptr == &hopping_frequency[2] ? hopping_frequency : hopping_frequency_ptr + 1;
|
||||
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
|
||||
}
|
||||
return 1200;
|
||||
#endif
|
||||
}
|
||||
|
||||
void devo_bind()
|
||||
void DEVO_init()
|
||||
{
|
||||
fixed_id = Model_fixed_id;
|
||||
bind_counter = DEVO_BIND_COUNT;
|
||||
use_fixed_id = 1;
|
||||
//PROTOCOL_SetBindState(0x1388 * 2400 / 1000); //msecs 12000ms
|
||||
}
|
||||
|
||||
/*
|
||||
void generate_fixed_id_bind(){
|
||||
if(BIND_0){
|
||||
//randomSeed((uint32_t)analogRead(A6)<<10|analogRead(A7));//seed
|
||||
uint8_t txid[4];
|
||||
//Model_fixed_id = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
|
||||
Model_fixed_id=0x332211;
|
||||
txid[0]= (id &0xFF);
|
||||
txid[1] = ((id >> 8) & 0xFF);
|
||||
txid[2] = ((id >> 16) & 0xFF);
|
||||
//txid[3] = ((id >> 24) & 0xFF);
|
||||
eeprom_write_block((const void*)txid,(void*)40,3);
|
||||
devo_bind();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
uint16_t DevoInit()
|
||||
#ifdef ENABLE_PPM
|
||||
if(mode_select) //PPM mode
|
||||
{
|
||||
CYRF_Reset();
|
||||
cyrf_init();
|
||||
CYRF_GetMfgData(cyrfmfg_id);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
CYRF_ConfigCRCSeed(0x0000);
|
||||
CYRF_ConfigSOPCode(sopcodes[0]);
|
||||
set_radio_channels();
|
||||
use_fixed_id = 0;
|
||||
failsafe_pkt = 0;
|
||||
hopping_frequency_ptr = hopping_frequency;
|
||||
//
|
||||
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
|
||||
//FIXME: Properly setnumber of channels;
|
||||
pkt_num = 0;
|
||||
ch_idx = 0;
|
||||
txState = 0;
|
||||
//uint8_t txid[4];
|
||||
//
|
||||
|
||||
/*
|
||||
if(BIND_0){
|
||||
Model_fixed_id=0;
|
||||
eeprom_write_block((const void*)0,(void*)40,4);
|
||||
while(1){
|
||||
LED_ON;
|
||||
delay(100);
|
||||
LED_OFF;
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
else{
|
||||
eeprom_read_block((void*)txid,(const void*)40,3);
|
||||
Model_fixed_id=(txid[0] | ((uint32_t)txid[1]<<8) | ((uint32_t)txid[2]<<16));
|
||||
}
|
||||
*/
|
||||
|
||||
if(! Model_fixed_id)
|
||||
{//model fixed ID =0
|
||||
fixed_id = ((uint32_t)(hopping_frequency[0] ^ cyrfmfg_id[0] ^ cyrfmfg_id[3]) << 16)
|
||||
| ((uint32_t)(hopping_frequency[1] ^ cyrfmfg_id[1] ^ cyrfmfg_id[4]) << 8)
|
||||
| ((uint32_t)(hopping_frequency[2] ^ cyrfmfg_id[2] ^ cyrfmfg_id[5]) << 0);
|
||||
fixed_id = fixed_id % 1000000;
|
||||
bind_counter = DEVO_BIND_COUNT;
|
||||
phase = DEVO_BIND;
|
||||
//PROTOCOL_SetBindState(0x1388 * 2400 / 1000); //msecs
|
||||
if(IS_BIND_BUTTON_FLAG_on)
|
||||
{
|
||||
eeprom_write_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num),0x00); // reset to autobind mode for the current model
|
||||
option=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
option=eeprom_read_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num)); // load previous mode: autobind or fixed id
|
||||
if(option!=1) option=0; // if not fixed id mode then it should be autobind
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_PPM
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case 1:
|
||||
num_ch=10;
|
||||
break;
|
||||
case 2:
|
||||
num_ch=12;
|
||||
break;
|
||||
case 3:
|
||||
num_ch=6;
|
||||
break;
|
||||
case 4:
|
||||
num_ch=7;
|
||||
break;
|
||||
default:
|
||||
num_ch=8;
|
||||
break;
|
||||
}
|
||||
DEVO_cyrf_init();
|
||||
CYRF_GetMfgData(cyrfmfg_id);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
CYRF_ConfigCRCSeed(0x0000);
|
||||
CYRF_PROGMEM_ConfigSOPCode(DEVO_j6pro_sopcodes[0]);
|
||||
DEVO_set_radio_channels();
|
||||
|
||||
hopping_frequency_ptr = hopping_frequency;
|
||||
CYRF_ConfigRFChannel(*hopping_frequency_ptr);
|
||||
|
||||
packet_count = 0;
|
||||
|
||||
prev_option=option;
|
||||
if(option==0)
|
||||
{
|
||||
MProtocol_id = ((uint32_t)(hopping_frequency[0] ^ cyrfmfg_id[0] ^ cyrfmfg_id[3]) << 16)
|
||||
| ((uint32_t)(hopping_frequency[1] ^ cyrfmfg_id[1] ^ cyrfmfg_id[4]) << 8)
|
||||
| ((uint32_t)(hopping_frequency[2] ^ cyrfmfg_id[2] ^ cyrfmfg_id[5]) << 0);
|
||||
MProtocol_id %= 1000000;
|
||||
bind_counter = DEVO_BIND_COUNT;
|
||||
phase = DEVO_BIND;
|
||||
BIND_IN_PROGRESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed_id = Model_fixed_id;
|
||||
use_fixed_id = 1;
|
||||
phase = DEVO_BOUND_1;
|
||||
bind_counter = 0;
|
||||
cyrf_set_bound_sop_code();
|
||||
DEVO_cyrf_set_bound_sop_code();
|
||||
}
|
||||
|
||||
return 2400;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
151
Multiprotocol/E010R5_cyrf6936.ino
Normal file
151
Multiprotocol/E010R5_cyrf6936.ino
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(E010R5_CYRF6936_INO)
|
||||
|
||||
#include "iface_rf2500.h"
|
||||
|
||||
#define E010R5_FORCE_ID
|
||||
|
||||
#define E010R5_PAYLOAD_SIZE 14
|
||||
|
||||
|
||||
static void __attribute__((unused)) E010R5_build_data_packet()
|
||||
{
|
||||
packet[ 0] = 0x0D; // Packet length
|
||||
packet[ 1] = convert_channel_8b(THROTTLE);
|
||||
packet[ 2] = convert_channel_s8b(RUDDER);
|
||||
packet[ 3] = convert_channel_s8b(ELEVATOR);
|
||||
packet[ 4] = convert_channel_s8b(AILERON);
|
||||
packet[ 5] = 0x20; // Trim Rudder
|
||||
packet[ 6] = 0x20; // Trim Elevator
|
||||
packet[ 7] = 0x20; // Trim Aileron
|
||||
packet[ 8] = 0x01 // Flags: high=0x01, low=0x00
|
||||
| GET_FLAG(CH5_SW, 0x04) // flip=0x04
|
||||
| GET_FLAG(CH6_SW, 0x08) // led=0x08
|
||||
| GET_FLAG(CH8_SW, 0x10) // headless=0x10
|
||||
| GET_FLAG(CH9_SW, 0x20); // one key return=0x20
|
||||
packet[ 9] = IS_BIND_IN_PROGRESS ? 0x80 : 0x00 // Flags: bind=0x80
|
||||
| GET_FLAG(CH7_SW, 0x20) // calib=0x20
|
||||
| GET_FLAG(CH10_SW, 0x01); // strange effect=0x01=long press on right button
|
||||
packet[10] = rx_tx_addr[0];
|
||||
packet[11] = rx_tx_addr[1];
|
||||
packet[12] = rx_tx_addr[2];
|
||||
packet[13] = 0x9D; // Check
|
||||
for(uint8_t i=0;i<13;i++)
|
||||
packet[13] += packet[i];
|
||||
|
||||
RF2500_BuildPayload(packet);
|
||||
}
|
||||
|
||||
uint16_t E010R5_callback()
|
||||
{
|
||||
//Bind
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
BIND_DONE;
|
||||
|
||||
//Send packet
|
||||
RF2500_SendPayload();
|
||||
|
||||
//Timing and hopping
|
||||
packet_count++;
|
||||
switch(packet_count)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
return 1183;
|
||||
default:
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no &= 3;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
rf_ch_num = 0x30 + (hopping_frequency_no<<3);
|
||||
else
|
||||
rf_ch_num = hopping_frequency[hopping_frequency_no];
|
||||
RF2500_RFChannel(rf_ch_num);
|
||||
RF2500_SetPower();
|
||||
packet_count = 0;
|
||||
case 3:
|
||||
E010R5_build_data_packet();
|
||||
return 3400;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void E010R5_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // Autobind protocol
|
||||
bind_counter = 2600;
|
||||
|
||||
//RF2500 emu init
|
||||
RF2500_Init(E010R5_PAYLOAD_SIZE, false); // 14 bytes, not scrambled
|
||||
RF2500_SetTXAddr((uint8_t*)"\x0E\x54\x96\xEE"); // Same address for bind and normal packets
|
||||
|
||||
rx_tx_addr[0]=0x00;
|
||||
hopping_frequency[0]=0x35; //53
|
||||
#ifdef E010R5_FORCE_ID
|
||||
switch(rx_tx_addr[3]%5)
|
||||
{
|
||||
case 0:
|
||||
//TX1
|
||||
//hopping_frequency[0]=0x35; //53
|
||||
hopping_frequency[1]=0x30; //48
|
||||
rx_tx_addr[1]=0x45;
|
||||
rx_tx_addr[2]=0x46;
|
||||
break;
|
||||
case 1:
|
||||
//TX2
|
||||
//hopping_frequency[0]=0x35; //53
|
||||
hopping_frequency[1]=0x3C; //60
|
||||
rx_tx_addr[1]=0x1B;
|
||||
rx_tx_addr[2]=0x9E;
|
||||
break;
|
||||
case 2:
|
||||
//TX4
|
||||
hopping_frequency[0]=0x30; //48
|
||||
hopping_frequency[1]=0x38; //56
|
||||
rx_tx_addr[1]=0x2E;
|
||||
rx_tx_addr[2]=0xAE;
|
||||
break;
|
||||
case 3:
|
||||
//TX5
|
||||
//hopping_frequency[0]=0x35; //53
|
||||
hopping_frequency[1]=0x41; //65
|
||||
rx_tx_addr[0]=0x0D;
|
||||
rx_tx_addr[1]=0xB9;
|
||||
rx_tx_addr[2]=0xFC;
|
||||
break;
|
||||
default:
|
||||
//TX3
|
||||
hopping_frequency[0]=0x30; //48
|
||||
hopping_frequency[1]=0x38; //56
|
||||
rx_tx_addr[1]=0x17;
|
||||
rx_tx_addr[2]=0x0D;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// This is the same as the E010 v1...
|
||||
hopping_frequency[2]=hopping_frequency[0]+0x10;
|
||||
hopping_frequency[3]=hopping_frequency[1]+0x10;
|
||||
|
||||
E010R5_build_data_packet();
|
||||
RF2500_RFChannel(hopping_frequency[0]);
|
||||
hopping_frequency_no=0;
|
||||
packet_count=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
157
Multiprotocol/E016HV2_cc2500.ino
Normal file
157
Multiprotocol/E016HV2_cc2500.ino
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(E016HV2_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define FORCE_E016HV2_ORIGINAL_ID
|
||||
|
||||
#define E016HV2_INITIAL_WAIT 500
|
||||
#define E016HV2_PACKET_PERIOD 10000
|
||||
#define E016HV2_RF_BIND_CHANNEL 5
|
||||
#define E016HV2_PAYLOAD_SIZE 11
|
||||
#define E016HV2_BIND_COUNT 300 //3sec
|
||||
|
||||
static void __attribute__((unused)) E016HV2_send_packet()
|
||||
{
|
||||
//payload length (after this byte)
|
||||
packet[0 ] = 0x0A;
|
||||
|
||||
//bind indicator
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[1 ] = 0x02;
|
||||
if(bind_counter)
|
||||
bind_counter--;
|
||||
else
|
||||
{
|
||||
BIND_DONE;
|
||||
CC2500_250K_RFChannel(rf_ch_num); // Set main channel
|
||||
}
|
||||
}
|
||||
else
|
||||
packet[1 ] = 0x20;
|
||||
|
||||
//ID
|
||||
packet[2 ] = rx_tx_addr[2];
|
||||
packet[3 ] = rx_tx_addr[3];
|
||||
|
||||
//channels TREA
|
||||
uint8_t channel;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
channel=0x64; // Throttle must be centered during bind
|
||||
else
|
||||
channel=convert_channel_8b_limit_deadband(THROTTLE,0x00,0x64,0xC8, 20);
|
||||
packet[4 ] = channel;
|
||||
channel=convert_channel_16b_limit(RUDDER,0x00,0xC8);
|
||||
packet[5 ] = channel;
|
||||
channel=convert_channel_16b_limit(ELEVATOR,0x00,0xC8);
|
||||
packet[6 ] = channel;
|
||||
channel=convert_channel_16b_limit(AILERON,0x00,0xC8);
|
||||
packet[7 ] = channel;
|
||||
|
||||
//flags
|
||||
if(CH8_SW && !phase) //toggle calib flag
|
||||
flags ^= 0x40;
|
||||
phase=CH8_SW;
|
||||
|
||||
packet[8 ] = GET_FLAG(CH7_SW, 0x01) // 0x01=Flip
|
||||
| GET_FLAG(CH9_SW, 0x02) // 0x02=Headless
|
||||
| GET_FLAG(CH10_SW, 0x04) // 0x04=One Key Return
|
||||
| flags; // 0x40=Calib
|
||||
|
||||
packet[9 ] = 0x02; // Speed control 0x00:low, 0x01:medium, 0x02:high
|
||||
|
||||
packet[10] = GET_FLAG(CH5_SW, 0x01) // 0x01=TakeOff/Land (momentary switch)
|
||||
| GET_FLAG(CH6_SW, 0x04); // 0x04=Emergeny Stop (momentary switch)
|
||||
|
||||
CC2500_SetPower(); // Set tx_power
|
||||
CC2500_SetFreqOffset(); // Set frequency offset
|
||||
|
||||
//Build real packet and send it
|
||||
static uint8_t pid=0;
|
||||
crc=0;
|
||||
|
||||
// stop TX/RX
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
// flush tx FIFO
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
// packet length
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO, 6 + 4 + 1 + 11 + 2); // preamble + address + packet_control + payload + crc
|
||||
|
||||
// preamble+address
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, (uint8_t*)"\xAA\xAA\xAA\xAA\xAA\xAA\xE7\xE7\xE7\xE7", 10);
|
||||
|
||||
// packet control
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO, 0x50+(pid<<2));
|
||||
pid++;
|
||||
|
||||
// payload
|
||||
//debug("P:")
|
||||
for (uint8_t i = 0; i < E016HV2_PAYLOAD_SIZE; ++i)
|
||||
{
|
||||
uint8_t byte = (bit_reverse(packet[i])<<1) | (packet[i+1]&0x01);
|
||||
//debug(" %02X",byte)
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO,byte);
|
||||
crc16_update(byte, 8);
|
||||
}
|
||||
|
||||
// crc
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO,crc >> 8);
|
||||
CC2500_WriteReg(CC2500_3F_TXFIFO,crc);
|
||||
//debugln(" %04X",crc)
|
||||
|
||||
// transmit
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
}
|
||||
|
||||
uint16_t E016HV2_callback()
|
||||
{
|
||||
E016HV2_send_packet();
|
||||
return E016HV2_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void E016HV2_init()
|
||||
{
|
||||
//Config CC2500
|
||||
CC2500_250K_Init();
|
||||
CC2500_250K_RFChannel(E016HV2_RF_BIND_CHANNEL); // Set bind channel
|
||||
|
||||
#ifdef FORCE_E016HV2_ORIGINAL_ID
|
||||
rx_tx_addr[2]=0x27;
|
||||
rx_tx_addr[3]=0x1B;
|
||||
//rf_ch_num = 44;
|
||||
#endif
|
||||
//General ID
|
||||
//3F1B -> 68,2C1B -> 49,2B1B -> 48,2A1B -> 47,291B -> 46,281B -> 45,271B -> 44,261B -> 43,251B -> 42
|
||||
//241B -> no bind,231B -> no bind,221B -> 71,211B -> 70,201B -> 69,1F1B -> 68,1E1B -> 67,1D1B -> 66,1C1B -> 65,1B1B -> 64,1A1B -> 63,191B -> 62,181B -> 61,171B -> 60,161B -> 59
|
||||
//0C1B -> 49,051B -> 42,041B -> no bind,031B -> no bind,021B -> 71,011B -> 70,001B -> no bind
|
||||
if(rx_tx_addr[2]<3) rx_tx_addr[2]+=3; // rx_tx_addr[2]=0 is invalid
|
||||
if(rx_tx_addr[3]==0) rx_tx_addr[3]+=64; // rx_tx_addr[3]=0 is invalid
|
||||
rf_ch_num = (rx_tx_addr[2] + rx_tx_addr[3]) % 32 + 42;
|
||||
if(rf_ch_num>71) // channels 72 and 73 are invalid
|
||||
{
|
||||
rx_tx_addr[2]-=2;
|
||||
rf_ch_num-=2;
|
||||
}
|
||||
|
||||
phase=CH8_SW;
|
||||
flags=0;
|
||||
bind_counter = E016HV2_BIND_COUNT;
|
||||
BIND_IN_PROGRESS; // Autobind protocol
|
||||
}
|
||||
|
||||
#endif
|
||||
332
Multiprotocol/E01X_nrf24l01.ino
Normal file
332
Multiprotocol/E01X_nrf24l01.ino
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with E012 and E015
|
||||
|
||||
#if defined(E01X_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
//Protocols constants
|
||||
#define E01X_BIND_COUNT 500
|
||||
#define E01X_INITIAL_WAIT 500
|
||||
#define E01X_ADDRESS_LENGTH 5
|
||||
|
||||
#define E012_PACKET_PERIOD 4525
|
||||
#define E012_RF_BIND_CHANNEL 0x3c
|
||||
#define E012_NUM_RF_CHANNELS 4
|
||||
#define E012_PACKET_SIZE 15
|
||||
|
||||
#define E015_PACKET_PERIOD 4500 // stock Tx=9000, but let's send more packets ...
|
||||
#define E015_RF_CHANNEL 0x2d // 2445 MHz
|
||||
#define E015_PACKET_SIZE 10
|
||||
#define E015_BIND_PACKET_SIZE 9
|
||||
|
||||
#define E016H_PACKET_PERIOD 4080
|
||||
#define E016H_PACKET_SIZE 10
|
||||
#define E016H_BIND_CHANNEL 80
|
||||
#define E016H_NUM_CHANNELS 4
|
||||
|
||||
//Channels
|
||||
#define E01X_ARM_SW CH5_SW
|
||||
#define E016H_STOP_SW CH5_SW
|
||||
#define E01X_FLIP_SW CH6_SW
|
||||
#define E01X_LED_SW CH7_SW
|
||||
#define E01X_HEADLESS_SW CH8_SW
|
||||
#define E01X_RTH_SW CH9_SW
|
||||
|
||||
// E012 flags packet[1]
|
||||
#define E012_FLAG_FLIP 0x40
|
||||
#define E012_FLAG_HEADLESS 0x10
|
||||
#define E012_FLAG_RTH 0x04
|
||||
// E012 flags packet[7]
|
||||
#define E012_FLAG_EXPERT 0x02
|
||||
|
||||
// E015 flags packet[6]
|
||||
#define E015_FLAG_DISARM 0x80
|
||||
#define E015_FLAG_ARM 0x40
|
||||
// E015 flags packet[7]
|
||||
#define E015_FLAG_FLIP 0x80
|
||||
#define E015_FLAG_HEADLESS 0x10
|
||||
#define E015_FLAG_RTH 0x08
|
||||
#define E015_FLAG_LED 0x04
|
||||
#define E015_FLAG_EXPERT 0x02
|
||||
#define E015_FLAG_INTERMEDIATE 0x01
|
||||
|
||||
// E016H flags packet[1]
|
||||
#define E016H_FLAG_CALIBRATE 0x80
|
||||
#define E016H_FLAG_STOP 0x20
|
||||
#define E016H_FLAG_FLIP 0x04
|
||||
// E016H flags packet[3]
|
||||
#define E016H_FLAG_HEADLESS 0x10
|
||||
#define E016H_FLAG_RTH 0x04
|
||||
// E016H flags packet[7]
|
||||
#define E016H_FLAG_TAKEOFF 0x80
|
||||
#define E016H_FLAG_HIGHRATE 0x08
|
||||
|
||||
static void __attribute__((unused)) E015_check_arming()
|
||||
{
|
||||
uint8_t arm_channel = E01X_ARM_SW;
|
||||
|
||||
if (arm_channel != arm_channel_previous)
|
||||
{
|
||||
arm_channel_previous = arm_channel;
|
||||
if (arm_channel)
|
||||
{
|
||||
armed = 1;
|
||||
arm_flags ^= E015_FLAG_ARM;
|
||||
}
|
||||
else
|
||||
{
|
||||
armed = 0;
|
||||
arm_flags ^= E015_FLAG_DISARM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) E01X_send_packet()
|
||||
{
|
||||
uint8_t can_flip = 0, calibrate = 1;
|
||||
if(sub_protocol==E012)
|
||||
{
|
||||
packet_length=E012_PACKET_SIZE;
|
||||
packet[0] = rx_tx_addr[1];
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[1] = 0xaa;
|
||||
memcpy(&packet[2], hopping_frequency, E012_NUM_RF_CHANNELS);
|
||||
memcpy(&packet[6], rx_tx_addr, E01X_ADDRESS_LENGTH);
|
||||
rf_ch_num=E012_RF_BIND_CHANNEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[1] = 0x01
|
||||
| GET_FLAG(E01X_RTH_SW, E012_FLAG_RTH)
|
||||
| GET_FLAG(E01X_HEADLESS_SW, E012_FLAG_HEADLESS)
|
||||
| GET_FLAG(E01X_FLIP_SW, E012_FLAG_FLIP);
|
||||
packet[2] = convert_channel_16b_limit(AILERON, 0xc8, 0x00); // aileron
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR, 0x00, 0xc8); // elevator
|
||||
packet[4] = convert_channel_16b_limit(RUDDER, 0xc8, 0x00); // rudder
|
||||
packet[5] = convert_channel_16b_limit(THROTTLE, 0x00, 0xc8); // throttle
|
||||
packet[6] = 0xaa;
|
||||
packet[7] = E012_FLAG_EXPERT; // rate (0-2)
|
||||
packet[8] = 0x00;
|
||||
packet[9] = 0x00;
|
||||
packet[10]= 0x00;
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no++];
|
||||
hopping_frequency_no %= E012_NUM_RF_CHANNELS;
|
||||
}
|
||||
packet[11] = 0x00;
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x56;
|
||||
packet[14] = rx_tx_addr[2];
|
||||
}
|
||||
else if(sub_protocol==E015)
|
||||
{ // E015
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0x18;
|
||||
packet[1] = 0x04;
|
||||
packet[2] = 0x06;
|
||||
// data phase address
|
||||
memcpy(&packet[3], rx_tx_addr, E01X_ADDRESS_LENGTH);
|
||||
// checksum
|
||||
packet[8] = packet[3];
|
||||
for(uint8_t i=4; i<8; i++)
|
||||
packet[8] += packet[i];
|
||||
packet_length=E015_BIND_PACKET_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
E015_check_arming();
|
||||
packet[0] = convert_channel_16b_limit(THROTTLE, 0, 225); // throttle
|
||||
packet[1] = convert_channel_16b_limit(RUDDER, 225, 0); // rudder
|
||||
packet[2] = convert_channel_16b_limit(AILERON, 0, 225); // aileron
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR, 225, 0); // elevator
|
||||
packet[4] = 0x20; // elevator trim
|
||||
packet[5] = 0x20; // aileron trim
|
||||
packet[6] = arm_flags;
|
||||
packet[7] = E015_FLAG_EXPERT
|
||||
| GET_FLAG(E01X_FLIP_SW, E015_FLAG_FLIP)
|
||||
| GET_FLAG(E01X_LED_SW, E015_FLAG_LED)
|
||||
| GET_FLAG(E01X_HEADLESS_SW,E015_FLAG_HEADLESS)
|
||||
| GET_FLAG(E01X_RTH_SW, E015_FLAG_RTH);
|
||||
packet[8] = 0;
|
||||
// checksum
|
||||
packet[9] = packet[0];
|
||||
for(uint8_t i=1; i<9; i++)
|
||||
packet[9] += packet[i];
|
||||
packet_length=E015_PACKET_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // E016H
|
||||
packet_length=E016H_PACKET_SIZE;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
rf_ch_num=E016H_BIND_CHANNEL;
|
||||
memcpy(packet, &rx_tx_addr[1], 4);
|
||||
memcpy(&packet[4], hopping_frequency, 4);
|
||||
packet[8] = 0x23;
|
||||
}
|
||||
else
|
||||
{
|
||||
// trim commands
|
||||
packet[0] = 0;
|
||||
// aileron
|
||||
uint16_t val = convert_channel_16b_limit(AILERON, 0, 0x3ff);
|
||||
can_flip |= (val < 0x100) || (val > 0x300);
|
||||
packet[1] = val >> 8;
|
||||
packet[2] = val & 0xff;
|
||||
if(val < 0x300) calibrate = 0;
|
||||
// elevator
|
||||
val = convert_channel_16b_limit(ELEVATOR, 0x3ff, 0);
|
||||
can_flip |= (val < 0x100) || (val > 0x300);
|
||||
packet[3] = val >> 8;
|
||||
packet[4] = val & 0xff;
|
||||
if(val < 0x300) calibrate = 0;
|
||||
// throttle
|
||||
val = convert_channel_16b_limit(THROTTLE, 0, 0x3ff);
|
||||
packet[5] = val >> 8;
|
||||
packet[6] = val & 0xff;
|
||||
if(val > 0x100) calibrate = 0;
|
||||
// rudder
|
||||
val = convert_channel_16b_limit(RUDDER, 0, 0x3ff);
|
||||
packet[7] = val >> 8;
|
||||
packet[8] = val & 0xff;
|
||||
if(val > 0x100) calibrate = 0;
|
||||
// flags
|
||||
packet[1] |= GET_FLAG(E016H_STOP_SW, E016H_FLAG_STOP)
|
||||
| (can_flip ? GET_FLAG(E01X_FLIP_SW, E016H_FLAG_FLIP) : 0)
|
||||
| (calibrate ? E016H_FLAG_CALIBRATE : 0);
|
||||
packet[3] |= GET_FLAG(E01X_HEADLESS_SW, E016H_FLAG_HEADLESS)
|
||||
| GET_FLAG(E01X_RTH_SW, E016H_FLAG_RTH);
|
||||
packet[7] |= E016H_FLAG_HIGHRATE;
|
||||
// frequency hopping
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no++ & 0x03];
|
||||
}
|
||||
// checksum
|
||||
packet[9] = packet[0];
|
||||
for (uint8_t i=1; i < E016H_PACKET_SIZE-1; i++)
|
||||
packet[9] += packet[i];
|
||||
}
|
||||
|
||||
// Power on, TX mode, CRC enabled
|
||||
if(sub_protocol==E016H)
|
||||
XN297_Configure( _BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
else //E012 & E015
|
||||
HS6200_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
if(sub_protocol==E016H)
|
||||
XN297_WritePayload(packet, packet_length);
|
||||
else
|
||||
HS6200_WritePayload(packet, packet_length);
|
||||
|
||||
// Check and adjust transmission power. We do this after
|
||||
// transmission to not bother with timeout after power
|
||||
// settings change - we have plenty of time until next
|
||||
// packet.
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) E01X_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if(sub_protocol==E012)
|
||||
HS6200_SetTXAddr((uint8_t *)"\x55\x42\x9C\x8F\xC9", E01X_ADDRESS_LENGTH);
|
||||
else if(sub_protocol==E015)
|
||||
HS6200_SetTXAddr((uint8_t *)"\x62\x54\x79\x38\x53", E01X_ADDRESS_LENGTH);
|
||||
else //E016H
|
||||
XN297_SetTXAddr((uint8_t *)"\x5a\x53\x46\x30\x31", 5); // bind address
|
||||
}
|
||||
|
||||
uint16_t E01X_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
if(sub_protocol==E016H)
|
||||
XN297_SetTXAddr(rx_tx_addr, E01X_ADDRESS_LENGTH);
|
||||
else
|
||||
HS6200_SetTXAddr(rx_tx_addr, E01X_ADDRESS_LENGTH);
|
||||
BIND_DONE;
|
||||
}
|
||||
}
|
||||
E01X_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) E012_initialize_txid()
|
||||
{
|
||||
// rf channels
|
||||
uint32_t lfsr=random(0xfefefefe);
|
||||
for(uint8_t i=0; i<E012_NUM_RF_CHANNELS; i++)
|
||||
{
|
||||
hopping_frequency[i] = 0x10 + ((lfsr & 0xff) % 0x32);
|
||||
lfsr>>=8;
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) E016H_initialize_txid()
|
||||
{
|
||||
// tx id
|
||||
rx_tx_addr[0] = 0xa5;
|
||||
rx_tx_addr[1] = 0x00;
|
||||
|
||||
// rf channels
|
||||
uint32_t lfsr=random(0xfefefefe);
|
||||
for(uint8_t i=0; i<E016H_NUM_CHANNELS; i++)
|
||||
{
|
||||
hopping_frequency[i] = (lfsr & 0xFF) % 80;
|
||||
lfsr>>=8;
|
||||
}
|
||||
}
|
||||
|
||||
void E01X_init()
|
||||
{
|
||||
BIND_IN_PROGRESS;
|
||||
if(sub_protocol==E012)
|
||||
{
|
||||
E012_initialize_txid();
|
||||
packet_period=E012_PACKET_PERIOD;
|
||||
}
|
||||
else if(sub_protocol==E015)
|
||||
{
|
||||
packet_period=E015_PACKET_PERIOD;
|
||||
rf_ch_num=E015_RF_CHANNEL;
|
||||
armed = 0;
|
||||
arm_flags = 0;
|
||||
arm_channel_previous = E01X_ARM_SW;
|
||||
}
|
||||
else
|
||||
{ // E016H
|
||||
E016H_initialize_txid();
|
||||
packet_period=E016H_PACKET_PERIOD;
|
||||
}
|
||||
E01X_RF_init();
|
||||
bind_counter = E01X_BIND_COUNT;
|
||||
hopping_frequency_no = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
148
Multiprotocol/E129_cyrf6936.ino
Normal file
148
Multiprotocol/E129_cyrf6936.ino
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(E129_CYRF6936_INO)
|
||||
|
||||
#include "iface_rf2500.h"
|
||||
|
||||
//#define E129_FORCE_ID
|
||||
|
||||
#define E129_BIND_CH 0x2D //45
|
||||
#define E129_PAYLOAD_SIZE 16
|
||||
|
||||
static void __attribute__((unused)) E129_build_data_packet()
|
||||
{
|
||||
//Build the packet
|
||||
memset(packet,0,E129_PAYLOAD_SIZE);
|
||||
packet[ 0] = 0x0F; // Packet length
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[ 1] = 0xA4;
|
||||
packet[ 2] = bit_reverse(rx_tx_addr[2]);
|
||||
packet[ 3] = bit_reverse(rx_tx_addr[3]);
|
||||
packet[ 4] = bit_reverse(rx_tx_addr[0]);
|
||||
packet[ 5] = bit_reverse(rx_tx_addr[1]);
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
packet[6+i]=hopping_frequency[i]-2;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[ 1] = 0xA6;
|
||||
packet[ 2] = 0xF7; // High rate 0xF7, low rate 0xF4
|
||||
//packet[ 3] = 0x00; // Mode: short press=0x20->0x00->0x20->..., long press=0x10->0x30->0x10->...
|
||||
packet[ 4] = GET_FLAG(CH5_SW, 0x20) // Take off/Land 0x20
|
||||
| GET_FLAG(CH6_SW, 0x04); // Emergency stop 0x04
|
||||
|
||||
uint16_t val = convert_channel_10b(AILERON,false);
|
||||
uint8_t trim = convert_channel_8b(CH7) & 0xFC;
|
||||
packet[ 5] = trim | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
|
||||
packet[ 6] = val; // channel (0x000...0x200...0x3FF)
|
||||
val = convert_channel_10b(ELEVATOR,false);
|
||||
trim = convert_channel_8b(CH8) & 0xFC;
|
||||
packet[ 7] = trim | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
|
||||
packet[ 8] = val; // channel (0x000...0x200...0x3FF)
|
||||
if(packet_count>200)
|
||||
val = convert_channel_10b(THROTTLE,false);
|
||||
else
|
||||
{//Allow bind to complete with throttle not centered
|
||||
packet_count++;
|
||||
val=0x200;
|
||||
}
|
||||
packet[ 9] = (0x1F<<2) | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
|
||||
packet[10] = val; // channel (0x000...0x200...0x3FF)
|
||||
val = convert_channel_10b(RUDDER,false);
|
||||
trim = convert_channel_8b(CH9) & 0xFC;
|
||||
packet[11] = trim | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
|
||||
packet[12] = val; // channel (0x000...0x200...0x3FF)
|
||||
}
|
||||
packet[14] = 0x00; // Check
|
||||
for(uint8_t i=0;i<14;i++)
|
||||
packet[14] += packet[i];
|
||||
|
||||
RF2500_BuildPayload(packet);
|
||||
}
|
||||
|
||||
uint16_t E129_callback()
|
||||
{
|
||||
//Set RF channel
|
||||
if(phase==0)
|
||||
RF2500_RFChannel(IS_BIND_IN_PROGRESS ? E129_BIND_CH : hopping_frequency[hopping_frequency_no]);
|
||||
|
||||
//Send packet
|
||||
RF2500_SendPayload();
|
||||
|
||||
//Send twice on same channel
|
||||
if(phase==0)
|
||||
{
|
||||
phase++;
|
||||
return 1260;
|
||||
}
|
||||
|
||||
//Bind
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
{
|
||||
BIND_DONE;
|
||||
RF2500_SetTXAddr(rx_tx_addr); // 4 bytes of address
|
||||
}
|
||||
|
||||
//Build packet
|
||||
E129_build_data_packet();
|
||||
|
||||
//Set power
|
||||
RF2500_SetPower();
|
||||
|
||||
//Hopp
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no &= 3;
|
||||
|
||||
phase=0;
|
||||
return 5200-1260;
|
||||
}
|
||||
|
||||
void E129_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // Autobind protocol
|
||||
bind_counter = 384; // ~2sec
|
||||
|
||||
//RF2500 emu init
|
||||
RF2500_Init(E129_PAYLOAD_SIZE, true); // 16 bytes, Scrambled
|
||||
|
||||
//Freq hopping
|
||||
calc_fh_channels(4);
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
if(hopping_frequency[i]==E129_BIND_CH)
|
||||
hopping_frequency[i]++;
|
||||
|
||||
#ifdef E129_FORCE_ID
|
||||
rx_tx_addr[0]=0xC1;
|
||||
rx_tx_addr[1]=0x22;
|
||||
rx_tx_addr[2]=0x05;
|
||||
rx_tx_addr[3]=0xA3;
|
||||
hopping_frequency[0]=0x3C; //60
|
||||
hopping_frequency[1]=0x49; //73
|
||||
hopping_frequency[2]=0x4B; //75
|
||||
hopping_frequency[3]=0x41; //65
|
||||
#endif
|
||||
|
||||
RF2500_SetTXAddr((uint8_t*)"\xE2\x32\xE0\xC8"); // 4 bytes of bind address
|
||||
|
||||
E129_build_data_packet();
|
||||
hopping_frequency_no=0;
|
||||
packet_count=0;
|
||||
phase=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
176
Multiprotocol/ESky150_nrf24l01.ino
Normal file
176
Multiprotocol/ESky150_nrf24l01.ino
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// ESky protocol for small models since 2014 (150, 300, 150X, ...)
|
||||
|
||||
#if defined(ESKY150_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define ESKY150_PAYLOADSIZE 15
|
||||
#define ESKY150_TX_ADDRESS_SIZE 4
|
||||
#define ESKY150_BINDING_PACKET_PERIOD 2000
|
||||
#define ESKY150_SENDING_PACKET_PERIOD 4800
|
||||
|
||||
static void __attribute__((unused)) ESKY150_RF_init()
|
||||
{
|
||||
//Original TX always sets for channelx 0x22 and 0x4a
|
||||
// Use channels 2..79
|
||||
hopping_frequency[0] = rx_tx_addr[3]%37+2;
|
||||
hopping_frequency[1] = hopping_frequency[0] + 40;
|
||||
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x02); // 4-byte RX/TX address
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_2M);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, ESKY150_PAYLOADSIZE); // bytes of data payload for pipe 0
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, ESKY150_TX_ADDRESS_SIZE);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
|
||||
// Enable: Dynamic Payload Length, Payload with ACK , W_TX_PAYLOAD_NOACK
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, _BV(NRF2401_1D_EN_DPL) | _BV(NRF2401_1D_EN_ACK_PAY) | _BV(NRF2401_1D_EN_DYN_ACK));
|
||||
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY150_bind_init()
|
||||
{
|
||||
uint8_t ESKY150_addr[ESKY150_TX_ADDRESS_SIZE] = { 0x73, 0x73, 0x74, 0x63 }; //This RX address "sstc" is fixed for ESky2
|
||||
|
||||
// Build packet
|
||||
packet[0] = rx_tx_addr[0];
|
||||
packet[1] = rx_tx_addr[1];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[4] = ESKY150_addr[0];
|
||||
packet[5] = ESKY150_addr[1];
|
||||
packet[6] = ESKY150_addr[2];
|
||||
packet[7] = ESKY150_addr[3];
|
||||
packet[8] = rx_tx_addr[0];
|
||||
packet[9] = rx_tx_addr[1];
|
||||
packet[10] = rx_tx_addr[2];
|
||||
packet[11] = rx_tx_addr[3];
|
||||
packet[12] = 0;
|
||||
packet[13] = 0;
|
||||
packet[14] = 0;
|
||||
|
||||
// Bind address
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, ESKY150_addr, ESKY150_TX_ADDRESS_SIZE);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, ESKY150_addr, ESKY150_TX_ADDRESS_SIZE);
|
||||
|
||||
// Bind Channel 1
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 1);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY150_send_packet()
|
||||
{
|
||||
// Build packet
|
||||
uint16_t throttle=convert_channel_16b_limit(THROTTLE,1000,2000);
|
||||
uint16_t aileron=convert_channel_16b_limit(AILERON,1000,2000);
|
||||
uint16_t elevator=convert_channel_16b_limit(ELEVATOR,1000,2000);
|
||||
uint16_t rudder=convert_channel_16b_limit(RUDDER,1000,2000);
|
||||
//set unused channels to zero, for compatibility with older 4 channel models
|
||||
uint8_t flight_mode=0;
|
||||
uint16_t aux_ch6=0;
|
||||
uint8_t aux_ch7=0;
|
||||
if(sub_protocol)
|
||||
{ // 7 channels
|
||||
flight_mode=ESKY150_convert_2bit_channel(CH5);
|
||||
aux_ch6=convert_channel_16b_limit(CH6,1000,2000);
|
||||
aux_ch7=ESKY150_convert_2bit_channel(CH7);
|
||||
}
|
||||
packet[0] = hopping_frequency[0];
|
||||
packet[1] = hopping_frequency[1];
|
||||
packet[2] = ((flight_mode << 6) & 0xC0) | ((aux_ch7 << 4) & 0x30) | ((throttle >> 8) & 0xFF);
|
||||
packet[3] = throttle & 0xFF;
|
||||
packet[4] = ((aux_ch6 >> 4) & 0xF0) | ((aileron >> 8) & 0xFF); //and 0xFF works as values are anyways not bigger than 12 bits, but faster code like that
|
||||
packet[5] = aileron & 0xFF;
|
||||
packet[6] = (aux_ch6 & 0xF0) | ((elevator >> 8) & 0xFF); //and 0xFF works as values are anyways not bigger than 12 bits, but faster code like that
|
||||
packet[7] = elevator & 0xFF;
|
||||
packet[8] = ((aux_ch6 << 4) & 0xF0) | ((rudder >> 8) & 0xFF); //and 0xFF works as values are anyways not bigger than 12 bits, but faster code like that
|
||||
packet[9] = rudder & 0xFF;
|
||||
// The next 4 Bytes are sint8 trim values (TAER). As trims are already included within normal outputs, these values are set to zero.
|
||||
packet[10] = 0x00;
|
||||
packet[11] = 0x00;
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
// Calculate checksum:
|
||||
uint8_t sum = 0;
|
||||
for (uint8_t i = 0; i < 14; ++i)
|
||||
sum += packet[i];
|
||||
packet[14] = sum;
|
||||
|
||||
// Hop on 2 channels
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no&=0x01;
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
|
||||
// Clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
// Send packet
|
||||
NRF24L01_WritePayload(packet, ESKY150_PAYLOADSIZE);
|
||||
|
||||
//Keep transmit power updated
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
uint8_t ESKY150_convert_2bit_channel(uint8_t num)
|
||||
{
|
||||
if(Channel_data[num] > CHANNEL_MAX_COMMAND)
|
||||
return 0x03;
|
||||
else
|
||||
if(Channel_data[num] < CHANNEL_MIN_COMMAND)
|
||||
return 0x00;
|
||||
else
|
||||
if(Channel_data[num] > CHANNEL_SWITCH)
|
||||
return 0x02;
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
uint16_t ESKY150_callback()
|
||||
{
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(ESKY150_SENDING_PACKET_PERIOD);
|
||||
#endif
|
||||
ESKY150_send_packet();
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF24L01_WritePayload(packet, ESKY150_PAYLOADSIZE);
|
||||
if (--bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
// Change TX address from bind to normal mode
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, ESKY150_TX_ADDRESS_SIZE);
|
||||
}
|
||||
return ESKY150_BINDING_PACKET_PERIOD;
|
||||
}
|
||||
return ESKY150_SENDING_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void ESKY150_init(void)
|
||||
{
|
||||
ESKY150_RF_init();
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
bind_counter=3000;
|
||||
ESKY150_bind_init();
|
||||
}
|
||||
hopping_frequency_no=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
138
Multiprotocol/ESky150v2_cc2500.ino
Normal file
138
Multiprotocol/ESky150v2_cc2500.ino
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(ESKY150V2_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define ESKY150V2_FORCE_ID
|
||||
|
||||
#define ESKY150V2_PAYLOADSIZE 40
|
||||
#define ESKY150V2_BINDPAYLOADSIZE 150
|
||||
#define ESKY150V2_NFREQCHANNELS 70
|
||||
#define ESKY150V2_TXID_SIZE 4
|
||||
#define ESKY150V2_BIND_CHANNEL 0x00
|
||||
#define ESKY150V2_PACKET_PERIOD 10000
|
||||
#define ESKY150V2_BINDING_PACKET_PERIOD 57000
|
||||
|
||||
#ifdef ESKY150V2_FORCE_ID
|
||||
const uint8_t PROGMEM ESKY150V2_hop[ESKY150V2_NFREQCHANNELS]= {
|
||||
0x07, 0x47, 0x09, 0x27, 0x0B, 0x42, 0x0D, 0x35, 0x17, 0x40, 0x26, 0x3D, 0x16, 0x43, 0x06, 0x2A, 0x24, 0x44,
|
||||
0x0E, 0x38, 0x20, 0x48, 0x22, 0x2D, 0x2B, 0x39, 0x0F, 0x36, 0x23, 0x46, 0x14, 0x3B, 0x1A, 0x41, 0x10, 0x2E,
|
||||
0x1E, 0x28, 0x0C, 0x49, 0x1D, 0x3E, 0x29, 0x2C, 0x25, 0x30, 0x1C, 0x2F, 0x1B, 0x33, 0x13, 0x31, 0x0A, 0x37,
|
||||
0x12, 0x3C, 0x18, 0x4B, 0x11, 0x45, 0x21, 0x4A, 0x1F, 0x3F, 0x15, 0x32, 0x08, 0x3A, 0x19, 0x34 };
|
||||
/*const uint8_t PROGMEM ESKY150V2_hop2[40]= {
|
||||
0x19, 0x23, 0x13, 0x1B, 0x09, 0x22, 0x14, 0x27, 0x06, 0x26, 0x16, 0x24, 0x0B, 0x2A, 0x0E, 0x1C, 0x11, 0x1E,
|
||||
0x08, 0x29, 0x0D, 0x28, 0x18, 0x2D, 0x12, 0x20, 0x0C, 0x1A, 0x10, 0x1D, 0x07, 0x2C, 0x0A, 0x2B, 0x0F, 0x25,
|
||||
0x15, 0x1F, 0x17, 0x21 };*/
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) ESKY150V2_set_freq(void)
|
||||
{
|
||||
calc_fh_channels(ESKY150V2_NFREQCHANNELS);
|
||||
|
||||
#ifdef ESKY150V2_FORCE_ID
|
||||
for(uint8_t i=0; i<ESKY150V2_NFREQCHANNELS; i++)
|
||||
hopping_frequency[i]=pgm_read_byte_near( &ESKY150V2_hop[i] );
|
||||
#endif
|
||||
|
||||
//Bind channel
|
||||
hopping_frequency[ESKY150V2_NFREQCHANNELS]=ESKY150V2_BIND_CHANNEL;
|
||||
|
||||
//Calib all channels
|
||||
CC2500_SetFreqOffset(); // Set frequency offset
|
||||
CC2500_250K_HoppingCalib(ESKY150V2_NFREQCHANNELS+1);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY150V2_send_packet()
|
||||
{
|
||||
CC2500_SetFreqOffset(); // Set frequency offset
|
||||
CC2500_250K_Hopping(hopping_frequency_no);
|
||||
if (++hopping_frequency_no >= ESKY150V2_NFREQCHANNELS)
|
||||
hopping_frequency_no = 0;
|
||||
CC2500_SetPower(); //Set power level
|
||||
|
||||
packet[0] = 0xFA; // Unknown
|
||||
packet[1] = 0x41; // Unknown
|
||||
packet[2] = 0x08; // Unknown
|
||||
packet[3] = 0x00; // Unknown
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
{
|
||||
uint16_t channel=convert_channel_16b_limit(CH_TAER[i],200,1000);
|
||||
packet[4+2*i] = channel;
|
||||
packet[5+2*i] = channel>>8;
|
||||
}
|
||||
CC2500_250K_NRF_WritePayload(packet, ESKY150V2_PAYLOADSIZE);
|
||||
}
|
||||
|
||||
uint16_t ESKY150V2_callback()
|
||||
{
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(ESKY150V2_PACKET_PERIOD);
|
||||
#endif
|
||||
ESKY150V2_send_packet();
|
||||
}
|
||||
else
|
||||
{
|
||||
BIND_DONE; //Need full power for bind to work...
|
||||
CC2500_SetPower(); //Set power level
|
||||
BIND_IN_PROGRESS;
|
||||
CC2500_250K_NRF_WritePayload(packet, ESKY150V2_BINDPAYLOADSIZE);
|
||||
if (--bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
// Change TX address from bind to normal mode
|
||||
CC2500_250K_NRF_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
|
||||
memset(packet,0x00,ESKY150V2_PAYLOADSIZE);
|
||||
}
|
||||
return 30000; //ESKY150V2_BINDING_PACKET_PERIOD;
|
||||
}
|
||||
return ESKY150V2_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void ESKY150V2_init()
|
||||
{
|
||||
CC2500_250K_Init();
|
||||
ESKY150V2_set_freq();
|
||||
hopping_frequency_no = 0;
|
||||
|
||||
#ifdef ESKY150V2_FORCE_ID // ID taken from TX dump
|
||||
rx_tx_addr[0]=0x87;rx_tx_addr[1]=0x5B;rx_tx_addr[2]=0x2C;rx_tx_addr[3]=0x5D;
|
||||
#endif
|
||||
|
||||
memset(packet,0x00,ESKY150V2_BINDPAYLOADSIZE);
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
CC2500_250K_NRF_SetTXAddr((uint8_t *)"\x73\x73\x74\x63", ESKY150V2_TXID_SIZE); //Bind address
|
||||
CC2500_250K_Hopping(ESKY150V2_NFREQCHANNELS); //Bind channel
|
||||
memcpy(packet,"\x73\x73\x74\x63", ESKY150V2_TXID_SIZE);
|
||||
memcpy(&packet[ESKY150V2_TXID_SIZE],rx_tx_addr, ESKY150V2_TXID_SIZE);
|
||||
packet[8]=0x41; //Unknown
|
||||
packet[9]=0x88; //Unknown
|
||||
packet[10]=0x41; //Unknown
|
||||
memset(&packet[11],0xAA,4); //Unknown
|
||||
memcpy(&packet[15],hopping_frequency,ESKY150V2_NFREQCHANNELS); // hop table
|
||||
//for(uint8_t i=0; i<40; i++) // Does not seem to be needed
|
||||
// packet[i+85]=pgm_read_byte_near( &ESKY150V2_hop2[i] );
|
||||
bind_counter=100;
|
||||
}
|
||||
else
|
||||
CC2500_250K_NRF_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
|
||||
}
|
||||
|
||||
#endif
|
||||
200
Multiprotocol/ESky_nrf24l01.ino
Normal file
200
Multiprotocol/ESky_nrf24l01.ino
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/esky_nrf24l01.c dated 2015-02-13
|
||||
|
||||
#if defined(ESKY_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
//#define ESKY_ET4_FORCE_ID
|
||||
|
||||
#define ESKY_BIND_COUNT 1000
|
||||
#define ESKY_STD_PACKET_PERIOD 3333
|
||||
#define ESKY_ET4_PACKET_PERIOD 1190
|
||||
#define ESKY_ET4_TOTAL_PACKET_PERIOD 20300
|
||||
#define ESKY_ET4_BIND_PACKET_PERIOD 5000
|
||||
#define ESKY_PAYLOAD_SIZE 13
|
||||
#define ESKY_PACKET_CHKTIME 100 // Time to wait for packet to be sent (no ACK, so very short)
|
||||
|
||||
static void __attribute__((unused)) ESKY_set_data_address()
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x02); // 4-byte RX/TX address for regular packets
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 4);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 4);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // 3-byte RX/TX address for bind packets
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"\x00\x00\x00", 3);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\x00\x00\x00", 3);
|
||||
}
|
||||
else
|
||||
ESKY_set_data_address();
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 50); // Channel 50 for bind packets
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, ESKY_PAYLOAD_SIZE); // bytes of data payload for pipe 0
|
||||
NRF24L01_WriteReg(NRF24L01_12_RX_PW_P1, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_13_RX_PW_P2, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_14_RX_PW_P3, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_15_RX_PW_P4, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_16_RX_PW_P5, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_17_FIFO_STATUS, 0x00); // Just in case, no real bits to write here
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY_TXID_init()
|
||||
{
|
||||
NRF24L01_FlushTx();
|
||||
if(sub_protocol==ESKY_STD)
|
||||
{
|
||||
uint16_t channel_ord = rx_tx_addr[0] % 74;
|
||||
hopping_frequency[12] = 10 + (uint8_t)channel_ord; //channel_code
|
||||
uint8_t channel1, channel2;
|
||||
channel1 = 10 + (uint8_t)((37 + channel_ord*5) % 74);
|
||||
channel2 = 10 + (uint8_t)(( channel_ord*5) % 74) ;
|
||||
|
||||
hopping_frequency[0] = channel1;
|
||||
hopping_frequency[1] = channel1;
|
||||
hopping_frequency[2] = channel1;
|
||||
hopping_frequency[3] = channel2;
|
||||
hopping_frequency[4] = channel2;
|
||||
hopping_frequency[5] = channel2;
|
||||
|
||||
//end_bytes
|
||||
hopping_frequency[6] = 6;
|
||||
hopping_frequency[7] = channel1*2;
|
||||
hopping_frequency[8] = channel2*2;
|
||||
hopping_frequency[9] = 6;
|
||||
hopping_frequency[10] = channel1*2;
|
||||
hopping_frequency[11] = channel2*2;
|
||||
}
|
||||
else
|
||||
{ // ESKY_ET4
|
||||
hopping_frequency[0] = 0x29; //41
|
||||
hopping_frequency[1] = 0x12; //18
|
||||
hopping_frequency[6] = 0x87; //135 payload end byte
|
||||
hopping_frequency[12] = 0x84; //132 indicates which channels to use
|
||||
}
|
||||
|
||||
// Turn radio power on
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) ESKY_send_packet(uint8_t bind)
|
||||
{
|
||||
uint8_t rf_ch = 50; // bind channel
|
||||
if (bind)
|
||||
{
|
||||
// Bind packet
|
||||
packet[0] = rx_tx_addr[2];
|
||||
packet[1] = rx_tx_addr[1];
|
||||
packet[2] = rx_tx_addr[0];
|
||||
packet[3] = hopping_frequency[12]; // channel_code encodes pair of channels to transmit on
|
||||
packet[4] = 0x18;
|
||||
packet[5] = 0x29;
|
||||
packet[6] = 0;
|
||||
packet[7] = 0;
|
||||
packet[8] = 0;
|
||||
packet[9] = 0;
|
||||
packet[10] = 0;
|
||||
packet[11] = 0;
|
||||
packet[12] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet_count == 0)
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
{
|
||||
uint16_t val=convert_channel_ppm(CH_AETR[i]);
|
||||
packet[i*2] = val>>8; //high byte of servo timing(1000-2000us)
|
||||
packet[i*2+1] = val&0xFF; //low byte of servo timing(1000-2000us)
|
||||
}
|
||||
if(sub_protocol==ESKY_STD)
|
||||
{
|
||||
// Regular packet
|
||||
// Each data packet is repeated 3 times on one channel, and 3 times on another channel
|
||||
// For arithmetic simplicity, channels are repeated in rf_channels array
|
||||
rf_ch = hopping_frequency[packet_count];
|
||||
packet[12] = hopping_frequency[packet_count+6]; // end_bytes
|
||||
packet_count++;
|
||||
if (packet_count > 6) packet_count = 0;
|
||||
}
|
||||
else
|
||||
{ // ESKY_ET4
|
||||
// Regular packet
|
||||
// Each data packet is repeated 14 times alternating between 2 channels
|
||||
rf_ch = hopping_frequency[packet_count&1];
|
||||
packet_count++;
|
||||
if(packet_count>14) packet_count=0;
|
||||
packet[12] = hopping_frequency[6]; // end_byte
|
||||
}
|
||||
}
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, ESKY_PAYLOAD_SIZE);
|
||||
NRF24L01_SetPower(); //Keep transmit power updated
|
||||
}
|
||||
|
||||
uint16_t ESKY_callback()
|
||||
{
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
if(packet_count==0)
|
||||
telemetry_set_input_sync(sub_protocol==ESKY_STD?ESKY_STD_PACKET_PERIOD*6:ESKY_ET4_TOTAL_PACKET_PERIOD);
|
||||
#endif
|
||||
ESKY_send_packet(0);
|
||||
if(sub_protocol==ESKY_ET4)
|
||||
{
|
||||
if(packet_count==0)
|
||||
return ESKY_ET4_TOTAL_PACKET_PERIOD-ESKY_ET4_PACKET_PERIOD*13;
|
||||
else
|
||||
return ESKY_ET4_PACKET_PERIOD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESKY_send_packet(1);
|
||||
if (--bind_counter == 0)
|
||||
{
|
||||
ESKY_set_data_address();
|
||||
BIND_DONE;
|
||||
}
|
||||
}
|
||||
return ESKY_STD_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void ESKY_init(void)
|
||||
{
|
||||
bind_counter = ESKY_BIND_COUNT;
|
||||
rx_tx_addr[2] = rx_tx_addr[3]; // Model match
|
||||
#ifdef ESKY_ET4_FORCE_ID
|
||||
if(sub_protocol==ESKY_ET4)
|
||||
{
|
||||
rx_tx_addr[0]=0x72;
|
||||
rx_tx_addr[1]=0xBB;
|
||||
rx_tx_addr[2]=0xCC;
|
||||
}
|
||||
#endif
|
||||
rx_tx_addr[3] = 0xBB;
|
||||
ESKY_RF_init();
|
||||
ESKY_TXID_init();
|
||||
packet_count=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
195
Multiprotocol/FQ777_nrf24l01.ino
Normal file
195
Multiprotocol/FQ777_nrf24l01.ino
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with bikemike FQ777-124.ino
|
||||
|
||||
#if defined(FQ777_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define FQ777_INITIAL_WAIT 500
|
||||
#define FQ777_PACKET_PERIOD 2000
|
||||
#define FQ777_PACKET_SIZE 8
|
||||
#define FQ777_BIND_COUNT 1000
|
||||
#define FQ777_NUM_RF_CHANNELS 4
|
||||
|
||||
enum {
|
||||
FQ777_FLAG_RETURN = 0x40, // 0x40 when not off, !0x40 when one key return
|
||||
FQ777_FLAG_HEADLESS = 0x04,
|
||||
FQ777_FLAG_EXPERT = 0x01,
|
||||
FQ777_FLAG_FLIP = 0x80,
|
||||
};
|
||||
|
||||
const uint8_t ssv_xor[] = {0x80,0x44,0x64,0x75,0x6C,0x71,0x2A,0x36,0x7C,0xF1,0x6E,0x52,0x9,0x9D,0x1F,0x78,0x3F,0xE1,0xEE,0x16,0x6D,0xE8,0x73,0x9,0x15,0xD7,0x92,0xE7,0x3,0xBA};
|
||||
uint8_t FQ777_bind_addr [] = {0xe7,0xe7,0xe7,0xe7,0x67};
|
||||
|
||||
static void __attribute__((unused)) ssv_pack_dpl(uint8_t addr[], uint8_t pid, uint8_t* len, uint8_t* payload, uint8_t* packed_payload)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
|
||||
uint16_t pcf = (*len & 0x3f) << 3;
|
||||
pcf |= (pid & 0x3) << 1;
|
||||
pcf |= 0x00; // noack field
|
||||
|
||||
uint8_t header[7] = {0};
|
||||
header[6] = pcf;
|
||||
header[5] = (pcf >> 7) | (addr[0] << 1);
|
||||
header[4] = (addr[0] >> 7) | (addr[1] << 1);
|
||||
header[3] = (addr[1] >> 7) | (addr[2] << 1);
|
||||
header[2] = (addr[2] >> 7) | (addr[3] << 1);
|
||||
header[1] = (addr[3] >> 7) | (addr[4] << 1);
|
||||
header[0] = (addr[4] >> 7);
|
||||
|
||||
// calculate the crc
|
||||
crc=0x3c18;
|
||||
for (i = 0; i < 7; ++i)
|
||||
crc16_update(header[i],8);
|
||||
for (i = 0; i < *len; ++i)
|
||||
crc16_update(payload[i],8);
|
||||
|
||||
// encode payload and crc
|
||||
// xor with this:
|
||||
for (i = 0; i < *len; ++i)
|
||||
payload[i] ^= ssv_xor[i];
|
||||
crc ^= ssv_xor[i++]<<8;
|
||||
crc ^= ssv_xor[i++];
|
||||
|
||||
// pack the pcf, payload, and crc into packed_payload
|
||||
packed_payload[0] = pcf >> 1;
|
||||
packed_payload[1] = (pcf << 7) | (payload[0] >> 1);
|
||||
|
||||
for (i = 0; i < *len - 1; ++i)
|
||||
packed_payload[i+2] = (payload[i] << 7) | (payload[i+1] >> 1);
|
||||
|
||||
packed_payload[i+2] = (payload[i] << 7) | (crc >> 9);
|
||||
++i;
|
||||
packed_payload[i+2] = (crc >> 1 & 0x80 ) | (crc >> 1 & 0x7F);
|
||||
++i;
|
||||
packed_payload[i+2] = (crc << 7);
|
||||
|
||||
*len += 4;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FQ777_send_packet()
|
||||
{
|
||||
uint8_t packet_len = FQ777_PACKET_SIZE;
|
||||
uint8_t packet_ori[8];
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
// 4,5,6 = address fields
|
||||
// last field is checksum of address fields
|
||||
packet_ori[0] = 0x20;
|
||||
packet_ori[1] = 0x15;
|
||||
packet_ori[2] = 0x05;
|
||||
packet_ori[3] = 0x06;
|
||||
packet_ori[4] = rx_tx_addr[0];
|
||||
packet_ori[5] = rx_tx_addr[1];
|
||||
packet_ori[6] = rx_tx_addr[2];
|
||||
packet_ori[7] = packet_ori[4] + packet_ori[5] + packet_ori[6];
|
||||
}
|
||||
else
|
||||
{
|
||||
// throt, yaw, pitch, roll, trims, flags/left button,00,right button
|
||||
//0-3 0x00-0x64
|
||||
//4 roll/pitch/yaw trims. cycles through one trim at a time - 0-40 trim1, 40-80 trim2, 80-C0 trim3 (center: A0 20 60)
|
||||
//5 flags for throttle button, two buttons above throttle - def: 0x40
|
||||
//6 00 ??
|
||||
//7 checksum - add values in other fields
|
||||
|
||||
|
||||
// Trims are usually done through the radio configuration but leaving the code here just in case...
|
||||
uint8_t trim_mod = packet_count % 144;
|
||||
uint8_t trim_val = 0;
|
||||
if (36 <= trim_mod && trim_mod < 72) // yaw
|
||||
trim_val = 0x20; // don't modify yaw trim
|
||||
else
|
||||
if (108 < trim_mod && trim_mod) // pitch
|
||||
trim_val = 0xA0;
|
||||
else // roll
|
||||
trim_val = 0x60;
|
||||
|
||||
packet_ori[0] = convert_channel_16b_limit(THROTTLE,0,0x64);
|
||||
packet_ori[1] = convert_channel_16b_limit(RUDDER,0,0x64);
|
||||
packet_ori[2] = convert_channel_16b_limit(ELEVATOR,0,0x64);
|
||||
packet_ori[3] = convert_channel_16b_limit(AILERON,0,0x64);
|
||||
packet_ori[4] = trim_val; // calculated above
|
||||
packet_ori[5] = GET_FLAG(CH5_SW, FQ777_FLAG_FLIP)
|
||||
| GET_FLAG(CH7_SW, FQ777_FLAG_HEADLESS)
|
||||
| GET_FLAG(!CH6_SW, FQ777_FLAG_RETURN)
|
||||
| GET_FLAG(CH8_SW,FQ777_FLAG_EXPERT);
|
||||
packet_ori[6] = 0x00;
|
||||
// calculate checksum
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i < 7; ++i)
|
||||
checksum += packet_ori[i];
|
||||
packet_ori[7] = checksum;
|
||||
|
||||
packet_count++;
|
||||
}
|
||||
|
||||
ssv_pack_dpl( IS_BIND_IN_PROGRESS ? FQ777_bind_addr : rx_tx_addr, hopping_frequency_no, &packet_len, packet_ori, packet);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG,_BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= FQ777_NUM_RF_CHANNELS;
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, packet_len);
|
||||
NRF24L01_WritePayload(packet, packet_len);
|
||||
NRF24L01_WritePayload(packet, packet_len);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FQ777_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, FQ777_bind_addr, 5);
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K);
|
||||
}
|
||||
|
||||
uint16_t FQ777_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(FQ777_PACKET_PERIOD);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
BIND_DONE;
|
||||
}
|
||||
}
|
||||
FQ777_send_packet();
|
||||
return FQ777_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void FQ777_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = FQ777_BIND_COUNT;
|
||||
packet_count=0;
|
||||
hopping_frequency[0] = 0x4D;
|
||||
hopping_frequency[1] = 0x43;
|
||||
hopping_frequency[2] = 0x27;
|
||||
hopping_frequency[3] = 0x07;
|
||||
hopping_frequency_no=0;
|
||||
rx_tx_addr[2] = 0x00;
|
||||
rx_tx_addr[3] = 0xe7;
|
||||
rx_tx_addr[4] = 0x67;
|
||||
FQ777_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
100
Multiprotocol/FX816_nrf24l01.ino
Normal file
100
Multiprotocol/FX816_nrf24l01.ino
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with FEI XIONG P38 plane.
|
||||
|
||||
#if defined(FX816_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define FX816_INITIAL_WAIT 500
|
||||
#define FX816_PACKET_PERIOD 10000
|
||||
#define FX816_RF_BIND_CHANNEL 0x28 //40
|
||||
#define FX816_RF_NUM_CHANNELS 4
|
||||
#define FX816_PAYLOAD_SIZE 6
|
||||
#define FX816_BIND_COUNT 300 //3sec
|
||||
|
||||
static void __attribute__((unused)) FX816_send_packet()
|
||||
{
|
||||
packet[0] = IS_BIND_IN_PROGRESS?0x55:0xAA;
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
uint8_t val=convert_channel_8b(AILERON);
|
||||
#define FX816_SWITCH 20
|
||||
if(val>127+FX816_SWITCH)
|
||||
packet[3] = 1;
|
||||
else if(val<127-FX816_SWITCH)
|
||||
packet[3] = 2;
|
||||
else
|
||||
packet[3] = 0;
|
||||
packet[4] = convert_channel_16b_limit(THROTTLE,0,100);
|
||||
val=0;
|
||||
for(uint8_t i=0;i<FX816_PAYLOAD_SIZE-1;i++)
|
||||
val+=packet[i];
|
||||
packet[5]=val;
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? FX816_RF_BIND_CHANNEL:hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no%=FX816_RF_NUM_CHANNELS;
|
||||
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, FX816_PAYLOAD_SIZE);
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FX816_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FX816_initialize_txid()
|
||||
{
|
||||
//Only 8 IDs: the RX led does not indicate frame loss.
|
||||
//I didn't open the plane to find out if I could connect there so this is the best I came up with with few trial and errors...
|
||||
rx_tx_addr[0]=0x35+(rx_tx_addr[3]&0x07); //Original dump=0x35
|
||||
rx_tx_addr[1]=0x09; //Original dump=0x09
|
||||
memcpy(hopping_frequency,"\x09\x1B\x30\x42",FX816_RF_NUM_CHANNELS); //Original dump=9=0x09,27=0x1B,48=0x30,66=0x42
|
||||
for(uint8_t i=0;i<FX816_RF_NUM_CHANNELS;i++)
|
||||
hopping_frequency[i]+=rx_tx_addr[3]&0x07;
|
||||
}
|
||||
|
||||
uint16_t FX816_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(FX816_PACKET_PERIOD);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
BIND_DONE;
|
||||
FX816_send_packet();
|
||||
return FX816_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void FX816_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
FX816_initialize_txid();
|
||||
FX816_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
bind_counter=FX816_BIND_COUNT;
|
||||
}
|
||||
|
||||
#endif
|
||||
218
Multiprotocol/FY326_nrf24l01.ino
Normal file
218
Multiprotocol/FY326_nrf24l01.ino
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/fy326_nrf24l01.c dated 2015-07-29
|
||||
|
||||
#if defined(FY326_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define FY326_INITIAL_WAIT 500
|
||||
#define FY326_PACKET_PERIOD 1500
|
||||
#define FY326_PACKET_CHKTIME 300
|
||||
#define FY326_PACKET_SIZE 15
|
||||
#define FY326_BIND_COUNT 16
|
||||
#define FY326_RF_BIND_CHANNEL 0x17
|
||||
#define FY326_NUM_RF_CHANNELS 5
|
||||
|
||||
enum {
|
||||
FY326_BIND1=0,
|
||||
FY326_BIND2,
|
||||
FY326_DATA,
|
||||
FY319_BIND1,
|
||||
FY319_BIND2,
|
||||
};
|
||||
|
||||
#define rxid channel
|
||||
|
||||
#define CHAN_TO_TRIM(chanval) ((chanval/10)-10)
|
||||
static void __attribute__((unused)) FY326_send_packet(uint8_t bind)
|
||||
{
|
||||
packet[0] = rx_tx_addr[3];
|
||||
if(bind)
|
||||
packet[1] = 0x55;
|
||||
else
|
||||
packet[1] = GET_FLAG(CH7_SW, 0x80) // Headless
|
||||
| GET_FLAG(CH6_SW, 0x40) // RTH
|
||||
| GET_FLAG(CH5_SW, 0x02) // Flip
|
||||
| GET_FLAG(CH9_SW, 0x01) // Calibrate
|
||||
| GET_FLAG(CH8_SW, 0x04); // Expert
|
||||
packet[2] = convert_channel_16b_limit(AILERON, 0, 200); // aileron
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR, 0, 200); // elevator
|
||||
packet[4] = convert_channel_16b_limit(RUDDER, 0, 200); // rudder
|
||||
packet[5] = convert_channel_16b_limit(THROTTLE, 0, 200); // throttle
|
||||
if(sub_protocol==FY319)
|
||||
{
|
||||
packet[6] = convert_channel_8b(AILERON);
|
||||
packet[7] = convert_channel_8b(ELEVATOR);
|
||||
packet[8] = convert_channel_8b(RUDDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[6] = rx_tx_addr[0];
|
||||
packet[7] = rx_tx_addr[1];
|
||||
packet[8] = rx_tx_addr[2];
|
||||
}
|
||||
packet[9] = CHAN_TO_TRIM(packet[2]); // aileron_trim;
|
||||
packet[10] = CHAN_TO_TRIM(packet[3]); // elevator_trim;
|
||||
packet[11] = CHAN_TO_TRIM(packet[4]); // rudder_trim;
|
||||
packet[12] = 0; // throttle_trim;
|
||||
packet[13] = rxid;
|
||||
packet[14] = rx_tx_addr[4];
|
||||
|
||||
if (bind)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, FY326_RF_BIND_CHANNEL);
|
||||
else
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= FY326_NUM_RF_CHANNELS;
|
||||
}
|
||||
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
NRF24L01_WritePayload(packet, FY326_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FY326_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if(sub_protocol==FY319)
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // Five-byte rx/tx address
|
||||
else
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x01); // Three-byte rx/tx address
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x15\x59\x23\xc6\x29", 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x15\x59\x23\xc6\x29", 5);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, FY326_PACKET_SIZE);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, FY326_RF_BIND_CHANNEL);
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f);
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
|
||||
|
||||
//Switch to RX
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
}
|
||||
|
||||
uint16_t FY326_callback()
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case FY319_BIND1:
|
||||
if(NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{
|
||||
NRF24L01_ReadPayload(packet, FY326_PACKET_SIZE);
|
||||
rxid = packet[13];
|
||||
packet[0] = rx_tx_addr[3];
|
||||
packet[1] = 0x80;
|
||||
packet[14]= rx_tx_addr[4];
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
bind_counter = 255;
|
||||
for(uint8_t i=2; i<6; i++)
|
||||
packet[i] = hopping_frequency[0];
|
||||
phase = FY319_BIND2;
|
||||
}
|
||||
return FY326_PACKET_CHKTIME;
|
||||
break;
|
||||
case FY319_BIND2:
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, FY326_PACKET_SIZE);
|
||||
if(bind_counter == 250)
|
||||
packet[1] = 0x40;
|
||||
if(--bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
phase = FY326_DATA;
|
||||
}
|
||||
break;
|
||||
case FY326_BIND1:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
NRF24L01_ReadPayload(packet, FY326_PACKET_SIZE);
|
||||
rxid = packet[13];
|
||||
rx_tx_addr[0] = 0xAA;
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
BIND_DONE;
|
||||
phase = FY326_DATA;
|
||||
}
|
||||
else
|
||||
if (bind_counter-- == 0)
|
||||
{
|
||||
bind_counter = FY326_BIND_COUNT;
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
FY326_send_packet(1);
|
||||
phase = FY326_BIND2;
|
||||
return FY326_PACKET_CHKTIME;
|
||||
}
|
||||
break;
|
||||
case FY326_BIND2:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS))
|
||||
{ // TX data sent -> switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
phase = FY326_BIND1;
|
||||
}
|
||||
else
|
||||
return FY326_PACKET_CHKTIME;
|
||||
break;
|
||||
case FY326_DATA:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(FY326_PACKET_PERIOD);
|
||||
#endif
|
||||
FY326_send_packet(0);
|
||||
break;
|
||||
}
|
||||
return FY326_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FY326_initialize_txid()
|
||||
{
|
||||
hopping_frequency[0] = (rx_tx_addr[0]&0x0f);
|
||||
hopping_frequency[1] = 0x10 + (rx_tx_addr[0] >> 4);
|
||||
hopping_frequency[2] = 0x20 + (rx_tx_addr[1]&0x0f);
|
||||
hopping_frequency[3] = 0x30 + (rx_tx_addr[1] >> 4);
|
||||
hopping_frequency[4] = 0x40 + (rx_tx_addr[2] >> 4);
|
||||
if(sub_protocol==FY319)
|
||||
for(uint8_t i=0;i<5;i++)
|
||||
hopping_frequency[i]=rx_tx_addr[0] & ~0x80;
|
||||
}
|
||||
|
||||
void FY326_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
rxid = 0xAA;
|
||||
bind_counter = FY326_BIND_COUNT;
|
||||
FY326_initialize_txid();
|
||||
FY326_RF_init();
|
||||
if(sub_protocol==FY319)
|
||||
{
|
||||
phase=FY319_BIND1;
|
||||
}
|
||||
else
|
||||
phase=FY326_BIND1;
|
||||
}
|
||||
|
||||
#endif
|
||||
108
Multiprotocol/Fake_nrf24l01.ino
Normal file
108
Multiprotocol/Fake_nrf24l01.ino
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(FAKE_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf250k.h"
|
||||
|
||||
static void __attribute__((unused)) FAKE_send_packet()
|
||||
{
|
||||
for(uint8_t i=0;i<5;i++)
|
||||
packet[i]=i;
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_RX_DR) | _BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT)));
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, 5);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FAKE_init()
|
||||
{
|
||||
// BIND_IN_PROGRESS;
|
||||
|
||||
//CC2500
|
||||
option=1;
|
||||
XN297L_Init();
|
||||
CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x01); // Packet Automation Control
|
||||
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x00); // Packet Automation Control
|
||||
CC2500_WriteReg(CC2500_12_MDMCFG2, 0x12); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_13_MDMCFG1, 0x13); // Modem Configuration
|
||||
CC2500_WriteReg(CC2500_04_SYNC1, 0x11);
|
||||
CC2500_WriteReg(CC2500_05_SYNC0, 0x33);
|
||||
CC2500_WriteReg(CC2500_09_ADDR, 0x99);
|
||||
CC2500_WriteReg(CC2500_06_PKTLEN, 10);
|
||||
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
//CC2500_SetTxRxMode(TX_EN);
|
||||
XN297L_RFChannel(0);
|
||||
|
||||
//NRF
|
||||
/* option=0;
|
||||
PE1_on; //NRF24L01 antenna RF3 by default
|
||||
PE2_off; //NRF24L01 antenna RF3 by default
|
||||
NRF24L01_Initialize();
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x7f);
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00);//0x3f); // AA on all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x3f); // Enable all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte address
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x36); // retransmit 1ms, 6 times
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 1Mbps
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t *)"\x99\x33\x11\xAA\xAA", 5); //Bind address
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t *)"\x99\x33\x11\xAA\xAA", 5); //Bind address
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0);
|
||||
NRF24L01_Activate(0x73); // Activate feature register
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // Enable dynamic payload length
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07); // Enable all features
|
||||
*/
|
||||
/*NRF24L01_FlushTx();
|
||||
NRF24L01_SetTxRxMode(TX_EN);*/
|
||||
}
|
||||
|
||||
uint16_t FAKE_callback()
|
||||
{
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if(len) debug("L=%d, ",len);
|
||||
if(len && len < sizeof(packet_in))
|
||||
{
|
||||
CC2500_ReadData(packet_in, len);
|
||||
debug("P:");
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
debug(" %02X", packet_in[i]);
|
||||
}
|
||||
if(len) debugln("");
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
|
||||
option=0;
|
||||
//FAKE_send_packet();
|
||||
|
||||
PE1_off; //antenna RF2
|
||||
PE2_on;
|
||||
/*packet[0]=0x99;
|
||||
for(uint8_t i=1;i<5;i++)
|
||||
packet[i]=i;
|
||||
CC2500_WriteData(packet, 5);*/
|
||||
return 10000;
|
||||
}
|
||||
|
||||
uint16_t initFAKE()
|
||||
{
|
||||
FAKE_init();
|
||||
return 5000;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/FLYSKY_a7105.c dated 2015-09-28
|
||||
|
||||
#if defined(FLYSKY_A7105_INO)
|
||||
|
||||
@@ -20,31 +21,12 @@
|
||||
//FlySky constants & variables
|
||||
#define FLYSKY_BIND_COUNT 2500
|
||||
|
||||
const uint8_t PROGMEM tx_channels[] = {
|
||||
0x0a, 0x5a, 0x14, 0x64, 0x1e, 0x6e, 0x28, 0x78, 0x32, 0x82, 0x3c, 0x8c, 0x46, 0x96, 0x50, 0xa0,
|
||||
0xa0, 0x50, 0x96, 0x46, 0x8c, 0x3c, 0x82, 0x32, 0x78, 0x28, 0x6e, 0x1e, 0x64, 0x14, 0x5a, 0x0a,
|
||||
0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x46, 0x96, 0x1e, 0x6e, 0x3c, 0x8c, 0x28, 0x78, 0x32, 0x82,
|
||||
0x82, 0x32, 0x78, 0x28, 0x8c, 0x3c, 0x6e, 0x1e, 0x96, 0x46, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a,
|
||||
0x28, 0x78, 0x0a, 0x5a, 0x50, 0xa0, 0x14, 0x64, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96,
|
||||
0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x64, 0x14, 0xa0, 0x50, 0x5a, 0x0a, 0x78, 0x28,
|
||||
0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x3c, 0x8c, 0x32, 0x82, 0x46, 0x96, 0x14, 0x64,
|
||||
0x64, 0x14, 0x96, 0x46, 0x82, 0x32, 0x8c, 0x3c, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50,
|
||||
0x50, 0xa0, 0x46, 0x96, 0x3c, 0x8c, 0x28, 0x78, 0x0a, 0x5a, 0x32, 0x82, 0x1e, 0x6e, 0x14, 0x64,
|
||||
0x64, 0x14, 0x6e, 0x1e, 0x82, 0x32, 0x5a, 0x0a, 0x78, 0x28, 0x8c, 0x3c, 0x96, 0x46, 0xa0, 0x50,
|
||||
0x46, 0x96, 0x3c, 0x8c, 0x50, 0xa0, 0x28, 0x78, 0x0a, 0x5a, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64,
|
||||
0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x5a, 0x0a, 0x78, 0x28, 0xa0, 0x50, 0x8c, 0x3c, 0x96, 0x46,
|
||||
0x46, 0x96, 0x0a, 0x5a, 0x3c, 0x8c, 0x14, 0x64, 0x50, 0xa0, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82,
|
||||
0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0xa0, 0x50, 0x64, 0x14, 0x8c, 0x3c, 0x5a, 0x0a, 0x96, 0x46,
|
||||
0x46, 0x96, 0x0a, 0x5a, 0x50, 0xa0, 0x3c, 0x8c, 0x28, 0x78, 0x1e, 0x6e, 0x32, 0x82, 0x14, 0x64,
|
||||
0x64, 0x14, 0x82, 0x32, 0x6e, 0x1e, 0x78, 0x28, 0x8c, 0x3c, 0xa0, 0x50, 0x5a, 0x0a, 0x96, 0x46
|
||||
};
|
||||
|
||||
enum {
|
||||
// flags going to byte 10
|
||||
FLAG_V9X9_VIDEO = 0x40,
|
||||
FLAG_V9X9_CAMERA= 0x80,
|
||||
// flags going to byte 12
|
||||
FLAG_V9X9_UNK = 0x10, // undocumented ?
|
||||
FLAG_V9X9_FLIP = 0x10,
|
||||
FLAG_V9X9_LED = 0x20,
|
||||
};
|
||||
|
||||
@@ -68,48 +50,45 @@ enum {
|
||||
FLAG_V912_BTMBTN= 0x80,
|
||||
};
|
||||
|
||||
uint8_t chanrow;
|
||||
uint8_t chancol;
|
||||
uint8_t chanoffset;
|
||||
|
||||
void flysky_apply_extension_flags()
|
||||
{
|
||||
const uint8_t V912_X17_SEQ[10] = { 0x14, 0x31, 0x40, 0x49, 0x49, // sometime first byte is 0x15 ?
|
||||
const uint8_t PROGMEM V912_X17_SEQ[10] = { 0x14, 0x31, 0x40, 0x49, 0x49, // sometime first byte is 0x15 ?
|
||||
0x49, 0x49, 0x49, 0x49, 0x49, };
|
||||
static uint8_t seq_counter;
|
||||
switch(sub_protocol) {
|
||||
|
||||
static void __attribute__((unused)) FLYSKY_apply_extension_flags()
|
||||
{
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case V9X9:
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[12] |= FLAG_V9X9_UNK;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
if(CH5_SW)
|
||||
packet[12] |= FLAG_V9X9_FLIP;
|
||||
if(CH6_SW)
|
||||
packet[12] |= FLAG_V9X9_LED;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
if(CH7_SW)
|
||||
packet[10] |= FLAG_V9X9_CAMERA;
|
||||
if(Servo_data[AUX4] > PPM_SWITCH)
|
||||
if(CH8_SW)
|
||||
packet[10] |= FLAG_V9X9_VIDEO;
|
||||
break;
|
||||
|
||||
case V6X6:
|
||||
packet[13] = 0x03; // 3 = 100% rate (0=40%, 1=60%, 2=80%)
|
||||
packet[14] = 0x00;
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
if(CH5_SW)
|
||||
packet[14] |= FLAG_V6X6_FLIP;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
if(CH6_SW)
|
||||
packet[14] |= FLAG_V6X6_LED;
|
||||
if(Servo_data[AUX3] > PPM_SWITCH)
|
||||
if(CH7_SW)
|
||||
packet[14] |= FLAG_V6X6_CAMERA;
|
||||
if(Servo_data[AUX4] > PPM_SWITCH)
|
||||
if(CH8_SW)
|
||||
packet[14] |= FLAG_V6X6_VIDEO;
|
||||
if(Servo_data[AUX5] > PPM_SWITCH)
|
||||
if(CH9_SW)
|
||||
{
|
||||
packet[13] |= FLAG_V6X6_HLESS1;
|
||||
packet[14] |= FLAG_V6X6_HLESS2;
|
||||
}
|
||||
if(Servo_data[AUX6] > PPM_SWITCH) //use option to manipulate these bytes
|
||||
if(CH10_SW)
|
||||
packet[14] |= FLAG_V6X6_RTH;
|
||||
if(Servo_data[AUX7] > PPM_SWITCH)
|
||||
if(CH11_SW)
|
||||
packet[14] |= FLAG_V6X6_XCAL;
|
||||
if(Servo_data[AUX8] > PPM_SWITCH)
|
||||
if(CH12_SW)
|
||||
packet[14] |= FLAG_V6X6_YCAL;
|
||||
packet[15] = 0x10; // unknown
|
||||
packet[16] = 0x10; // unknown
|
||||
@@ -120,20 +99,20 @@ void flysky_apply_extension_flags()
|
||||
break;
|
||||
|
||||
case V912:
|
||||
seq_counter++;
|
||||
if( seq_counter > 9)
|
||||
seq_counter = 0;
|
||||
packet_count++;
|
||||
if( packet_count > 9)
|
||||
packet_count = 0;
|
||||
packet[12] |= 0x20; // bit 6 is always set ?
|
||||
packet[13] = 0x00; // unknown
|
||||
packet[14] = 0x00;
|
||||
if(Servo_data[AUX1] > PPM_SWITCH)
|
||||
packet[14] |= FLAG_V912_BTMBTN;
|
||||
if(Servo_data[AUX2] > PPM_SWITCH)
|
||||
if(CH5_SW)
|
||||
packet[14] = FLAG_V912_BTMBTN;
|
||||
if(CH6_SW)
|
||||
packet[14] |= FLAG_V912_TOPBTN;
|
||||
packet[15] = 0x27; // [15] and [16] apparently hold an analog channel with a value lower than 1000
|
||||
packet[16] = 0x03; // maybe it's there for a pitch channel for a CP copter ?
|
||||
packet[17] = V912_X17_SEQ[seq_counter]; // not sure what [17] & [18] are for
|
||||
if(seq_counter == 0) // V912 Rx does not even read those bytes... [17-20]
|
||||
packet[17] = pgm_read_byte( &V912_X17_SEQ[packet_count] ) ; // not sure what [17] & [18] are for
|
||||
if(packet_count == 0) // V912 Rx does not even read those bytes... [17-20]
|
||||
packet[18] = 0x02;
|
||||
else
|
||||
packet[18] = 0x00;
|
||||
@@ -141,70 +120,123 @@ void flysky_apply_extension_flags()
|
||||
packet[20] = 0x00; // unknown
|
||||
break;
|
||||
|
||||
case CX20:
|
||||
packet[19] = 0x00; // unknown
|
||||
packet[20] = (hopping_frequency_no<<4)|0x0A;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flysky_build_packet(uint8_t init)
|
||||
static void __attribute__((unused)) FLYSKY_send_packet()
|
||||
{
|
||||
uint8_t i;
|
||||
//servodata timing range for flysky.
|
||||
////-100% =~ 0x03e8//=1000us(min)
|
||||
//-100% =~ 0x03e8//=1000us(min)
|
||||
//+100% =~ 0x07ca//=1994us(max)
|
||||
//Center = 0x5d9//=1497us(center)
|
||||
//channel order AIL;ELE;THR;RUD;AUX1;AUX2;AUX3;AUX4
|
||||
packet[0] = init ? 0xaa : 0x55;
|
||||
//channel order AIL;ELE;THR;RUD;CH5;CH6;CH7;CH8
|
||||
packet[0] = IS_BIND_IN_PROGRESS ? 0xaa : 0x55;
|
||||
packet[1] = rx_tx_addr[3];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = rx_tx_addr[1];
|
||||
packet[4] = rx_tx_addr[0];
|
||||
uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
packet[5+2*i]=lowByte(Servo_data[ch[i]]); //low byte of servo timing(1000-2000us)
|
||||
packet[6+2*i]=highByte(Servo_data[ch[i]]); //high byte of servo timing(1000-2000us)
|
||||
uint16_t temp=convert_channel_ppm(CH_AETR[i]);
|
||||
if(sub_protocol == CX20 && i==CH2) //ELEVATOR
|
||||
temp=3000-temp;
|
||||
packet[5 + i*2]=temp&0xFF; //low byte of servo timing(1000-2000us)
|
||||
packet[6 + i*2]=(temp>>8)&0xFF; //high byte of servo timing(1000-2000us)
|
||||
}
|
||||
flysky_apply_extension_flags();
|
||||
FLYSKY_apply_extension_flags();
|
||||
|
||||
A7105_SetPower();
|
||||
A7105_WriteData(21, IS_BIND_IN_PROGRESS ? 0x01:hopping_frequency[hopping_frequency_no & 0x0F]);
|
||||
hopping_frequency_no++;
|
||||
}
|
||||
|
||||
uint16_t ReadFlySky()
|
||||
uint16_t FLYSKY_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
#ifndef FORCE_FLYSKY_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
flysky_build_packet(1);
|
||||
A7105_WriteData(21, 1);
|
||||
bind_counter--;
|
||||
if (! bind_counter)
|
||||
if (bind_counter==0)
|
||||
BIND_DONE;
|
||||
}
|
||||
else
|
||||
FLYSKY_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM FLYSKY_tx_channels[8][4] = {
|
||||
{ 0x12, 0x34, 0x56, 0x78},
|
||||
{ 0x18, 0x27, 0x36, 0x45},
|
||||
{ 0x41, 0x82, 0x36, 0x57},
|
||||
{ 0x84, 0x13, 0x65, 0x72},
|
||||
{ 0x87, 0x64, 0x15, 0x32},
|
||||
{ 0x76, 0x84, 0x13, 0x52},
|
||||
{ 0x71, 0x62, 0x84, 0x35},
|
||||
{ 0x71, 0x86, 0x43, 0x52}
|
||||
};
|
||||
|
||||
void FLYSKY_init()
|
||||
{
|
||||
flysky_build_packet(0);
|
||||
A7105_WriteData(21, pgm_read_byte_near(&tx_channels[chanrow*16+chancol])-chanoffset);
|
||||
chancol = (chancol + 1) % 16;
|
||||
if (! chancol) //Keep transmit power updated
|
||||
A7105_SetPower();
|
||||
}
|
||||
return 1460;
|
||||
}
|
||||
uint8_t chanrow;
|
||||
uint8_t chanoffset;
|
||||
uint8_t temp;
|
||||
|
||||
uint16_t initFlySky() {
|
||||
//A7105_Reset();
|
||||
A7105_Init(INIT_FLYSKY); //flysky_init();
|
||||
A7105_Init();
|
||||
|
||||
if (rx_tx_addr[3] > 0x90) // limit offset to 9 as higher values don't work with some RX (ie V912)
|
||||
// limit offset to 9 as higher values don't work with some RX (ie V912)
|
||||
// limit offset to 9 as CX20 repeats the same channels after that
|
||||
if ((rx_tx_addr[3]&0xF0) > 0x90)
|
||||
rx_tx_addr[3]=rx_tx_addr[3]-0x70;
|
||||
chanrow=rx_tx_addr[3] % 16;
|
||||
chancol=0;
|
||||
|
||||
// Build frequency hop table
|
||||
chanrow=rx_tx_addr[3] & 0x0F;
|
||||
chanoffset=rx_tx_addr[3]/16;
|
||||
|
||||
|
||||
if(IS_AUTOBIND_FLAG_on)
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
{
|
||||
temp=pgm_read_byte_near(&FLYSKY_tx_channels[chanrow>>1][i>>2]);
|
||||
if(i&0x02)
|
||||
temp&=0x0F;
|
||||
else
|
||||
temp>>=4;
|
||||
temp*=0x0A;
|
||||
if(i&0x01)
|
||||
temp+=0x50;
|
||||
if(sub_protocol==CX20)
|
||||
{
|
||||
if(temp==0x0A)
|
||||
temp+=0x37;
|
||||
if(temp==0xA0)
|
||||
{
|
||||
if (chanoffset<4)
|
||||
temp=0x37;
|
||||
else if (chanoffset<9)
|
||||
temp=0x2D;
|
||||
else
|
||||
temp=0x29;
|
||||
}
|
||||
}
|
||||
hopping_frequency[((chanrow&1)?15-i:i)]=temp-chanoffset;
|
||||
}
|
||||
hopping_frequency_no=0;
|
||||
packet_count=0;
|
||||
if(sub_protocol==CX20)
|
||||
packet_period=3984;
|
||||
else
|
||||
packet_period=1510; //1460 on deviation but not working with the latest V911 bricks... Turnigy 9X v2 is 1533, Flysky TX for 9XR/9XR Pro is 1510, V911 TX is 1490.
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
bind_counter = FLYSKY_BIND_COUNT;
|
||||
else
|
||||
bind_counter = 0;
|
||||
return 2400;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
545
Multiprotocol/FrSkyDVX_common.ino
Normal file
545
Multiprotocol/FrSkyDVX_common.ino
Normal file
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/******************************/
|
||||
/** FrSky D and X routines **/
|
||||
/******************************/
|
||||
|
||||
#if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO) || defined(FRSKY_RX_CC2500_INO) || defined(FRSKYR9_SX1276_INO)
|
||||
uint8_t FrSkyFormat=0;
|
||||
uint8_t FrSkyX_chanskip;
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO) || defined(FRSKY_RX_CC2500_INO) || defined(FRSKYR9_SX1276_INO)
|
||||
//**CRC**
|
||||
const uint16_t PROGMEM FrSkyX_CRC_Short[]={
|
||||
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
|
||||
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7 };
|
||||
static uint16_t __attribute__((unused)) FrSkyX_CRCTable(uint8_t val)
|
||||
{
|
||||
uint16_t word ;
|
||||
word = pgm_read_word(&FrSkyX_CRC_Short[val&0x0F]) ;
|
||||
val /= 16 ;
|
||||
return word ^ (0x1081 * val) ;
|
||||
}
|
||||
uint16_t FrSkyX_crc(uint8_t *data, uint8_t len, uint16_t init=0)
|
||||
{
|
||||
uint16_t crc = init;
|
||||
for(uint8_t i=0; i < len; i++)
|
||||
crc = (crc<<8) ^ FrSkyX_CRCTable((uint8_t)(crc>>8) ^ *data++);
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYX_CC2500_INO) || defined(FRSKYR9_SX1276_INO)
|
||||
static void __attribute__((unused)) FrSkyX_channels(uint8_t offset)
|
||||
{
|
||||
static uint8_t chan_start=0;
|
||||
|
||||
//packet[7] = FLAGS 00 - standard packet
|
||||
//10, 12, 14, 16, 18, 1A, 1C, 1E - failsafe packet
|
||||
//20 - range check packet
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
#define FRSKYX_FAILSAFE_TIMEOUT 1032
|
||||
static uint16_t failsafe_count=0;
|
||||
static uint8_t FS_flag=0,failsafe_chan=0;
|
||||
if (FS_flag == 0 && failsafe_count > FRSKYX_FAILSAFE_TIMEOUT && chan_start == 0 && IS_FAILSAFE_VALUES_on)
|
||||
{
|
||||
FS_flag = 0x10;
|
||||
failsafe_chan = 0;
|
||||
} else if (FS_flag & 0x10 && failsafe_chan < (FrSkyFormat & 0x01 ? 8-1:16-1))
|
||||
{
|
||||
FS_flag = 0x10 | ((FS_flag + 2) & 0x0F); //10, 12, 14, 16, 18, 1A, 1C, 1E - failsafe packet
|
||||
failsafe_chan ++;
|
||||
} else if (FS_flag & 0x10)
|
||||
{
|
||||
FS_flag = 0;
|
||||
failsafe_count = 0;
|
||||
FAILSAFE_VALUES_off;
|
||||
}
|
||||
failsafe_count++;
|
||||
if(protocol==PROTO_FRSKY_R9)
|
||||
failsafe_count++; // R9 is 20ms, X is 9ms
|
||||
packet[offset] = FS_flag;
|
||||
#else
|
||||
packet[offset] = 0;
|
||||
#endif
|
||||
//
|
||||
packet[offset+1] = 0; //??
|
||||
//
|
||||
uint8_t chan_index = chan_start;
|
||||
uint16_t ch1,ch2;
|
||||
for(uint8_t i = offset+2; i < 12+offset+2 ; i+=3)
|
||||
{//12 bytes of channel data
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if( (FS_flag & 0x10) && ((failsafe_chan & 0x07) == (chan_index & 0x07)) )
|
||||
ch1 = FrSkyX_scaleForPXX_FS(failsafe_chan);
|
||||
else
|
||||
#endif
|
||||
ch1 = FrSkyX_scaleForPXX(chan_index);
|
||||
chan_index++;
|
||||
//
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if( (FS_flag & 0x10) && ((failsafe_chan & 0x07) == (chan_index & 0x07)) )
|
||||
ch2 = FrSkyX_scaleForPXX_FS(failsafe_chan);
|
||||
else
|
||||
#endif
|
||||
ch2 = FrSkyX_scaleForPXX(chan_index);
|
||||
chan_index++;
|
||||
//3 bytes per channel
|
||||
packet[i] = ch1;
|
||||
packet[i+1]=(((ch1>>8) & 0x0F)|(ch2 << 4));
|
||||
packet[i+2]=ch2>>4;
|
||||
}
|
||||
if(FrSkyFormat & 0x01) //In X8 mode send only 8ch every 9ms
|
||||
chan_start = 0 ;
|
||||
else
|
||||
chan_start^=0x08;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO) || defined(FRSKY_RX_CC2500_INO)
|
||||
enum {
|
||||
FRSKY_BIND = 0,
|
||||
FRSKY_BIND_DONE = 1000,
|
||||
FRSKY_DATA1,
|
||||
FRSKY_DATA2,
|
||||
FRSKY_DATA3,
|
||||
FRSKY_DATA4,
|
||||
FRSKY_DATA5,
|
||||
};
|
||||
|
||||
void Frsky_init_hop(void)
|
||||
{
|
||||
uint8_t val;
|
||||
uint8_t channel = rx_tx_addr[0]&0x07;
|
||||
uint8_t channel_spacing = rx_tx_addr[1];
|
||||
//Filter bad tables
|
||||
if(channel_spacing<0x02) channel_spacing+=0x02;
|
||||
if(channel_spacing>0xE9) channel_spacing-=0xE7;
|
||||
if(channel_spacing%0x2F==0) channel_spacing++;
|
||||
|
||||
hopping_frequency[0]=channel;
|
||||
for(uint8_t i=1;i<50;i++)
|
||||
{
|
||||
channel=(channel+channel_spacing) % 0xEB;
|
||||
val=channel;
|
||||
if((val==0x00) || (val==0x5A) || (val==0xDC))
|
||||
val++;
|
||||
hopping_frequency[i]=i>46?0:val;
|
||||
}
|
||||
}
|
||||
|
||||
void FrSkyX2_init_hop(void)
|
||||
{
|
||||
uint16_t id=(rx_tx_addr[2]<<8) + rx_tx_addr[3];
|
||||
//Increment
|
||||
uint8_t inc = (id % 46) + 1;
|
||||
if( inc == 12 || inc ==35 ) inc++; //Exception list from dumps
|
||||
//Start offset
|
||||
uint8_t offset = id % 5;
|
||||
|
||||
debug("hop: ");
|
||||
uint8_t channel;
|
||||
for(uint8_t i=0; i<47; i++)
|
||||
{
|
||||
channel = 5 * (uint16_t(inc * i) % 47) + offset;
|
||||
//Exception list from dumps
|
||||
if(FrSkyFormat & 2 )// LBT or FCC
|
||||
{//LBT
|
||||
if( channel <=1 || channel == 43 || channel == 44 || channel == 87 || channel == 88 || channel == 129 || channel == 130 || channel == 173 || channel == 174)
|
||||
channel += 2;
|
||||
else if( channel == 216 || channel == 217 || channel == 218)
|
||||
channel += 3;
|
||||
}
|
||||
else //FCC
|
||||
if ( channel == 3 || channel == 4 || channel == 46 || channel == 47 || channel == 90 || channel == 91 || channel == 133 || channel == 134 || channel == 176 || channel == 177 || channel == 220 || channel == 221 )
|
||||
channel += 2;
|
||||
//Store
|
||||
hopping_frequency[i] = channel;
|
||||
debug(" %02X",channel);
|
||||
}
|
||||
debugln("");
|
||||
hopping_frequency[47] = 0; //Bind freq
|
||||
}
|
||||
|
||||
void Frsky_init_clone(void)
|
||||
{
|
||||
debugln("Clone mode");
|
||||
uint16_t temp = FRSKYD_CLONE_EEPROM_OFFSET;
|
||||
if(protocol==PROTO_FRSKYX)
|
||||
temp=FRSKYX_CLONE_EEPROM_OFFSET;
|
||||
else if(protocol==PROTO_FRSKYX2)
|
||||
temp=FRSKYX2_CLONE_EEPROM_OFFSET;
|
||||
FrSkyFormat=eeprom_read_byte((EE_ADDR)temp++);
|
||||
/* FRSKY_RX_D8 =0,
|
||||
FRSKY_RX_D16FCC =1,
|
||||
FRSKY_RX_D16LBT =2,
|
||||
FRSKY_RX_D16v2FCC =3,
|
||||
FRSKY_RX_D16v2LBT =4,*/
|
||||
if(protocol==PROTO_FRSKYX)
|
||||
FrSkyFormat >>= 1;
|
||||
else
|
||||
FrSkyFormat >>= 2;
|
||||
FrSkyFormat <<= 1; //FCC_16/LBT_16
|
||||
if(sub_protocol==XCLONE_8)
|
||||
FrSkyFormat++; //FCC_8/LBT_8
|
||||
rx_tx_addr[3] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
rx_tx_addr[2] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
rx_tx_addr[1] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
memset(hopping_frequency,0x00,50);
|
||||
if(protocol!=PROTO_FRSKYX2)
|
||||
{//D8 and D16v1
|
||||
for (uint8_t ch = 0; ch < 47; ch++)
|
||||
hopping_frequency[ch] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
}
|
||||
else
|
||||
FrSkyX2_init_hop();
|
||||
}
|
||||
|
||||
#endif
|
||||
/******************************/
|
||||
/** FrSky V, D and X routines **/
|
||||
/******************************/
|
||||
#if defined(FRSKYV_CC2500_INO) || defined(FRSKYD_CC2500_INO) || defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO)
|
||||
const PROGMEM uint8_t FRSKY_common_startreg_cc2500_conf[]= {
|
||||
CC2500_02_IOCFG0 ,
|
||||
CC2500_00_IOCFG2 ,
|
||||
CC2500_17_MCSM1 ,
|
||||
CC2500_18_MCSM0 ,
|
||||
CC2500_06_PKTLEN ,
|
||||
CC2500_07_PKTCTRL1 ,
|
||||
CC2500_08_PKTCTRL0 ,
|
||||
CC2500_3E_PATABLE ,
|
||||
CC2500_0B_FSCTRL1 ,
|
||||
CC2500_0C_FSCTRL0 , // replaced by option value
|
||||
CC2500_0D_FREQ2 ,
|
||||
CC2500_0E_FREQ1 ,
|
||||
CC2500_0F_FREQ0 ,
|
||||
CC2500_10_MDMCFG4 ,
|
||||
CC2500_11_MDMCFG3 ,
|
||||
CC2500_12_MDMCFG2 ,
|
||||
CC2500_13_MDMCFG1 ,
|
||||
CC2500_14_MDMCFG0 ,
|
||||
CC2500_15_DEVIATN };
|
||||
|
||||
#if defined(FRSKYV_CC2500_INO)
|
||||
const PROGMEM uint8_t FRSKYV_cc2500_conf[]= {
|
||||
/*02_IOCFG0*/ 0x06 ,
|
||||
/*00_IOCFG2*/ 0x06 ,
|
||||
/*17_MCSM1*/ 0x0c ,
|
||||
/*18_MCSM0*/ 0x18 ,
|
||||
/*06_PKTLEN*/ 0xff ,
|
||||
/*07_PKTCTRL1*/ 0x04 ,
|
||||
/*08_PKTCTRL0*/ 0x05 ,
|
||||
/*3E_PATABLE*/ 0xfe ,
|
||||
/*0B_FSCTRL1*/ 0x08 ,
|
||||
/*0C_FSCTRL0*/ 0x00 ,
|
||||
/*0D_FREQ2*/ 0x5c ,
|
||||
/*0E_FREQ1*/ 0x58 ,
|
||||
/*0F_FREQ0*/ 0x9d ,
|
||||
/*10_MDMCFG4*/ 0xAA ,
|
||||
/*11_MDMCFG3*/ 0x10 ,
|
||||
/*12_MDMCFG2*/ 0x93 ,
|
||||
/*13_MDMCFG1*/ 0x23 ,
|
||||
/*14_MDMCFG0*/ 0x7a ,
|
||||
/*15_DEVIATN*/ 0x41 };
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYD_CC2500_INO)
|
||||
const PROGMEM uint8_t FRSKYD_cc2500_conf[]= {
|
||||
/*02_IOCFG0*/ 0x06 ,
|
||||
/*00_IOCFG2*/ 0x06 ,
|
||||
/*17_MCSM1*/ 0x0c ,
|
||||
/*18_MCSM0*/ 0x18 ,
|
||||
/*06_PKTLEN*/ 0x19 ,
|
||||
/*07_PKTCTRL1*/ 0x04 ,
|
||||
/*08_PKTCTRL0*/ 0x05 ,
|
||||
/*3E_PATABLE*/ 0xff ,
|
||||
/*0B_FSCTRL1*/ 0x08 ,
|
||||
/*0C_FSCTRL0*/ 0x00 ,
|
||||
/*0D_FREQ2*/ 0x5c ,
|
||||
/*0E_FREQ1*/ 0x76 ,
|
||||
/*0F_FREQ0*/ 0x27 ,
|
||||
/*10_MDMCFG4*/ 0xAA ,
|
||||
/*11_MDMCFG3*/ 0x39 ,
|
||||
/*12_MDMCFG2*/ 0x11 ,
|
||||
/*13_MDMCFG1*/ 0x23 ,
|
||||
/*14_MDMCFG0*/ 0x7a ,
|
||||
/*15_DEVIATN*/ 0x42 };
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO)
|
||||
const PROGMEM uint8_t FRSKYX_cc2500_conf[]= {
|
||||
//FRSKYX
|
||||
/*02_IOCFG0*/ 0x06 ,
|
||||
/*00_IOCFG2*/ 0x06 ,
|
||||
/*17_MCSM1*/ 0x0c , //X2->0x0E -> RX stays in RX and TX stays in TX???
|
||||
/*18_MCSM0*/ 0x18 ,
|
||||
/*06_PKTLEN*/ 0x1E ,
|
||||
/*07_PKTCTRL1*/ 0x04 ,
|
||||
/*08_PKTCTRL0*/ 0x01 , //X2->0x05 -> CRC enabled
|
||||
/*3E_PATABLE*/ 0xff ,
|
||||
/*0B_FSCTRL1*/ 0x0A ,
|
||||
/*0C_FSCTRL0*/ 0x00 ,
|
||||
/*0D_FREQ2*/ 0x5c ,
|
||||
/*0E_FREQ1*/ 0x76 ,
|
||||
/*0F_FREQ0*/ 0x27 ,
|
||||
/*10_MDMCFG4*/ 0x7B ,
|
||||
/*11_MDMCFG3*/ 0x61 , //X2->0x84 -> bitrate 70K->77K
|
||||
/*12_MDMCFG2*/ 0x13 ,
|
||||
/*13_MDMCFG1*/ 0x23 ,
|
||||
/*14_MDMCFG0*/ 0x7a ,
|
||||
/*15_DEVIATN*/ 0x51 };
|
||||
const PROGMEM uint8_t FRSKYXEU_cc2500_conf[]= {
|
||||
/*02_IOCFG0*/ 0x06 ,
|
||||
/*00_IOCFG2*/ 0x06 ,
|
||||
/*17_MCSM1*/ 0x0E ,
|
||||
/*18_MCSM0*/ 0x18 ,
|
||||
/*06_PKTLEN*/ 0x23 ,
|
||||
/*07_PKTCTRL1*/ 0x04 ,
|
||||
/*08_PKTCTRL0*/ 0x01 , //X2->0x05 -> CRC enabled
|
||||
/*3E_PATABLE*/ 0xff ,
|
||||
/*0B_FSCTRL1*/ 0x08 ,
|
||||
/*0C_FSCTRL0*/ 0x00 ,
|
||||
/*0D_FREQ2*/ 0x5c ,
|
||||
/*0E_FREQ1*/ 0x80 ,
|
||||
/*0F_FREQ0*/ 0x00 ,
|
||||
/*10_MDMCFG4*/ 0x7B ,
|
||||
/*11_MDMCFG3*/ 0xF8 ,
|
||||
/*12_MDMCFG2*/ 0x03 ,
|
||||
/*13_MDMCFG1*/ 0x23 ,
|
||||
/*14_MDMCFG0*/ 0x7a ,
|
||||
/*15_DEVIATN*/ 0x53 };
|
||||
const PROGMEM uint8_t FRSKYL_cc2500_conf[]= {
|
||||
/*02_IOCFG0*/ 0x02 ,
|
||||
/*00_IOCFG2*/ 0x02 ,
|
||||
/*17_MCSM1*/ 0x0C ,
|
||||
/*18_MCSM0*/ 0x18 ,
|
||||
/*06_PKTLEN*/ 0xFF ,
|
||||
/*07_PKTCTRL1*/ 0x00 ,
|
||||
/*08_PKTCTRL0*/ 0x02 ,
|
||||
/*3E_PATABLE*/ 0xFE ,
|
||||
/*0B_FSCTRL1*/ 0x0A ,
|
||||
/*0C_FSCTRL0*/ 0x00 ,
|
||||
/*0D_FREQ2*/ 0x5c ,
|
||||
/*0E_FREQ1*/ 0x76 ,
|
||||
/*0F_FREQ0*/ 0x27 ,
|
||||
/*10_MDMCFG4*/ 0x5C ,
|
||||
/*11_MDMCFG3*/ 0x3B ,
|
||||
/*12_MDMCFG2*/ 0x00 ,
|
||||
/*13_MDMCFG1*/ 0x03 ,
|
||||
/*14_MDMCFG0*/ 0x7A ,
|
||||
/*15_DEVIATN*/ 0x47 };
|
||||
#endif
|
||||
|
||||
const PROGMEM uint8_t FRSKY_common_end_cc2500_conf[][2]= {
|
||||
{ CC2500_19_FOCCFG, 0x16 },
|
||||
{ CC2500_1A_BSCFG, 0x6c },
|
||||
{ CC2500_1B_AGCCTRL2, 0x43 },
|
||||
{ CC2500_1C_AGCCTRL1, 0x40 },
|
||||
{ CC2500_1D_AGCCTRL0, 0x91 },
|
||||
{ CC2500_21_FREND1, 0x56 },
|
||||
{ CC2500_22_FREND0, 0x10 },
|
||||
{ CC2500_23_FSCAL3, 0xa9 },
|
||||
{ CC2500_24_FSCAL2, 0x0A },
|
||||
{ CC2500_25_FSCAL1, 0x00 },
|
||||
{ CC2500_26_FSCAL0, 0x11 },
|
||||
{ CC2500_29_FSTEST, 0x59 },
|
||||
{ CC2500_2C_TEST2, 0x88 },
|
||||
{ CC2500_2D_TEST1, 0x31 },
|
||||
{ CC2500_2E_TEST0, 0x0B },
|
||||
{ CC2500_03_FIFOTHR, 0x07 },
|
||||
{ CC2500_09_ADDR, 0x00 } };
|
||||
|
||||
void FRSKY_init_cc2500(const uint8_t *ptr)
|
||||
{
|
||||
for(uint8_t i=0;i<19;i++)
|
||||
{
|
||||
uint8_t reg=pgm_read_byte_near(&FRSKY_common_startreg_cc2500_conf[i]);
|
||||
uint8_t val=pgm_read_byte_near(&ptr[i]);
|
||||
if(reg==CC2500_0C_FSCTRL0)
|
||||
val=option;
|
||||
CC2500_WriteReg(reg,val);
|
||||
}
|
||||
for(uint8_t i=0;i<17;i++)
|
||||
{
|
||||
uint8_t reg=pgm_read_byte_near(&FRSKY_common_end_cc2500_conf[i][0]);
|
||||
uint8_t val=pgm_read_byte_near(&FRSKY_common_end_cc2500_conf[i][1]);
|
||||
CC2500_WriteReg(reg,val);
|
||||
}
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
CC2500_Strobe(CC2500_SIDLE); // Go to idle...
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYX_CC2500_INO) || defined(FRSKYR9_SX1276_INO)
|
||||
uint8_t FrSkyX_TX_Seq, FrSkyX_TX_IN_Seq;
|
||||
uint8_t FrSkyX_RX_Seq ;
|
||||
|
||||
#ifdef SPORT_SEND
|
||||
struct t_FrSkyX_TX_Frame
|
||||
{
|
||||
uint8_t count;
|
||||
uint8_t payload[8];
|
||||
} ;
|
||||
// Store FrskyX telemetry
|
||||
struct t_FrSkyX_TX_Frame FrSkyX_TX_Frames[4] ;
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_seq_sport(uint8_t start, uint8_t end)
|
||||
{
|
||||
for (uint8_t i=start+1;i<=end;i++)
|
||||
packet[i]=0;
|
||||
packet[start] = FrSkyX_RX_Seq << 4;
|
||||
#ifdef SPORT_SEND
|
||||
if (FrSkyX_TX_IN_Seq!=0xFF)
|
||||
{//RX has replied at least once
|
||||
if (FrSkyX_TX_IN_Seq & 0x08)
|
||||
{//Request init
|
||||
//debugln("Init");
|
||||
FrSkyX_TX_Seq = 0 ;
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
FrSkyX_TX_Frames[i].count=0; //Discard frames in current output buffer
|
||||
}
|
||||
else if (FrSkyX_TX_IN_Seq & 0x04)
|
||||
{//Retransmit the requested packet
|
||||
debugln("Retry:%d",FrSkyX_TX_IN_Seq&0x03);
|
||||
packet[start] |= FrSkyX_TX_IN_Seq&0x03;
|
||||
packet[start+1] = FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq&0x03].count;
|
||||
for (uint8_t i=start+2;i<start+2+FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq&0x03].count;i++)
|
||||
packet[i] = FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq&0x03].payload[i];
|
||||
}
|
||||
else if ( FrSkyX_TX_Seq != 0x08 )
|
||||
{
|
||||
if(FrSkyX_TX_Seq==FrSkyX_TX_IN_Seq)
|
||||
{//Send packet from the incoming radio buffer
|
||||
//debugln("Send:%d",FrSkyX_TX_Seq);
|
||||
packet[start] |= FrSkyX_TX_Seq;
|
||||
uint8_t nbr_bytes=0;
|
||||
for (uint8_t i=start+2;i<=end;i++)
|
||||
{
|
||||
if(SportHead==SportTail)
|
||||
break; //buffer empty
|
||||
packet[i]=SportData[SportHead];
|
||||
FrSkyX_TX_Frames[FrSkyX_TX_Seq].payload[i-start+2]=SportData[SportHead];
|
||||
SportHead=(SportHead+1) & (MAX_SPORT_BUFFER-1);
|
||||
nbr_bytes++;
|
||||
}
|
||||
packet[start+1]=nbr_bytes;
|
||||
FrSkyX_TX_Frames[FrSkyX_TX_Seq].count=nbr_bytes;
|
||||
if(nbr_bytes)
|
||||
{//Check the buffer status
|
||||
uint8_t used = SportTail;
|
||||
if ( SportHead > SportTail )
|
||||
used += MAX_SPORT_BUFFER - SportHead ;
|
||||
else
|
||||
used -= SportHead ;
|
||||
if ( used < (MAX_SPORT_BUFFER>>1) )
|
||||
{
|
||||
DATA_BUFFER_LOW_off;
|
||||
debugln("Ok buf:%d",used);
|
||||
}
|
||||
}
|
||||
FrSkyX_TX_Seq = ( FrSkyX_TX_Seq + 1 ) & 0x03 ; //Next iteration send next packet
|
||||
}
|
||||
else
|
||||
{//Not in sequence somehow, transmit what the receiver wants but why not asking for retransmit...
|
||||
//debugln("RX_Seq:%d,TX:%d",FrSkyX_TX_IN_Seq,FrSkyX_TX_Seq);
|
||||
packet[start] |= FrSkyX_TX_IN_Seq;
|
||||
packet[start+1] = FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq].count;
|
||||
for (uint8_t i=start+2;i<start+2+FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq].count;i++)
|
||||
packet[i] = FrSkyX_TX_Frames[FrSkyX_TX_IN_Seq].payload[i-start+2];
|
||||
}
|
||||
}
|
||||
else
|
||||
packet[start] |= 0x08 ; //FrSkyX_TX_Seq=8 at startup
|
||||
}
|
||||
if(packet[start+1])
|
||||
{//Debug
|
||||
debug("SP: ");
|
||||
for(uint8_t i=0;i<packet[start+1];i++)
|
||||
debug("%02X ",packet[start+2+i]);
|
||||
debugln("");
|
||||
}
|
||||
#else
|
||||
packet[start] |= FrSkyX_TX_Seq ;//TX=8 at startup
|
||||
if ( !(FrSkyX_TX_IN_Seq & 0xF8) )
|
||||
FrSkyX_TX_Seq = ( FrSkyX_TX_Seq + 1 ) & 0x03 ; // Next iteration send next packet
|
||||
#endif // SPORT_SEND
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_telem_init(void)
|
||||
{
|
||||
FrSkyX_TX_Seq = 0x08 ; // Request init
|
||||
#ifdef SPORT_SEND
|
||||
FrSkyX_TX_IN_Seq = 0xFF ; // No sequence received yet
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
FrSkyX_TX_Frames[i].count=0;// discard frames in current output buffer
|
||||
SportHead=SportTail=0; // empty data buffer
|
||||
#endif
|
||||
FrSkyX_RX_Seq = 0 ; // Seq 0 to start with
|
||||
#ifdef TELEMETRY
|
||||
telemetry_lost=1;
|
||||
telemetry_link=0; //Stop sending telemetry
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FRSKYX_CC2500_INO) || defined(FRSKYL_CC2500_INO)
|
||||
static void __attribute__((unused)) FrSkyX_set_start(uint8_t ch )
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[ch]);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[ch]);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_RF_init()
|
||||
{
|
||||
if(protocol==PROTO_FRSKYL)
|
||||
FRSKY_init_cc2500(FRSKYL_cc2500_conf);
|
||||
else
|
||||
FRSKY_init_cc2500((FrSkyFormat&2)?FRSKYXEU_cc2500_conf:FRSKYX_cc2500_conf); // LBT or FCC
|
||||
if(protocol==PROTO_FRSKYX2)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC
|
||||
if(!(FrSkyFormat&2))
|
||||
{ // FCC
|
||||
CC2500_WriteReg(CC2500_17_MCSM1, 0x0E); //0x0E -> RX stays in RX and TX stays in TX???
|
||||
CC2500_WriteReg(CC2500_11_MDMCFG3, 0x84); // bitrate 70K->77K
|
||||
}
|
||||
}
|
||||
//
|
||||
for(uint8_t c=0;c < 48;c++)
|
||||
{//calibrate hop channels
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR,hopping_frequency[c]);
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
delayMicroseconds(900);//
|
||||
calData[c] = CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
}
|
||||
//#######END INIT########
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_initialize_data(uint8_t adr)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_18_MCSM0, 0x8);
|
||||
CC2500_WriteReg(CC2500_09_ADDR, adr ? 0x03 : rx_tx_addr[3]);
|
||||
CC2500_WriteReg(CC2500_07_PKTCTRL1,0x05); // check address
|
||||
}
|
||||
|
||||
#endif
|
||||
214
Multiprotocol/FrSkyD_cc2500.ino
Normal file
214
Multiprotocol/FrSkyD_cc2500.ino
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(FRSKYD_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
static void __attribute__((unused)) FRSKYD_RF_init()
|
||||
{
|
||||
FRSKY_init_cc2500(FRSKYD_cc2500_conf);
|
||||
|
||||
CC2500_WriteReg(CC2500_1B_AGCCTRL2, IS_BIND_IN_PROGRESS ? 0x43 : 0x03);
|
||||
CC2500_WriteReg(CC2500_09_ADDR, IS_BIND_IN_PROGRESS ? 0x03 : rx_tx_addr[3]);
|
||||
CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05);
|
||||
CC2500_Strobe(CC2500_SIDLE); // Go to idle...
|
||||
//
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
//#######END INIT########
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKYD_build_bind_packet()
|
||||
{
|
||||
//11 03 01 d7 2d 00 00 1e 3c 5b 78 00 00 00 00 00 00 01
|
||||
//11 03 01 19 3e 00 02 8e 2f bb 5c 00 00 00 00 00 00 01
|
||||
packet[0] = 0x11;
|
||||
packet[1] = 0x03;
|
||||
packet[2] = 0x01;
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[4] = rx_tx_addr[2];
|
||||
uint16_t idx = ((state -FRSKY_BIND) % 10) * 5;
|
||||
packet[5] = idx;
|
||||
packet[6] = hopping_frequency[idx++];
|
||||
packet[7] = hopping_frequency[idx++];
|
||||
packet[8] = hopping_frequency[idx++];
|
||||
packet[9] = hopping_frequency[idx++];
|
||||
packet[10] = hopping_frequency[idx++];
|
||||
packet[11] = 0x00;
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x00;
|
||||
packet[16] = 0x00;
|
||||
packet[17] = rx_tx_addr[1];
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKYD_data_frame()
|
||||
{//pachet[4] is telemetry user frame counter(hub)
|
||||
//11 d7 2d 22 00 01 c9 c9 ca ca 88 88 ca ca c9 ca 88 88
|
||||
//11 57 12 00 00 01 f2 f2 f2 f2 06 06 ca ca ca ca 18 18
|
||||
packet[0] = 0x11; //Length
|
||||
packet[1] = rx_tx_addr[3];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = counter;//
|
||||
#if defined TELEMETRY
|
||||
packet[4] = telemetry_counter;
|
||||
#else
|
||||
packet[4] = 0x00;
|
||||
#endif
|
||||
|
||||
packet[5] = rx_tx_addr[1];
|
||||
//
|
||||
packet[10] = 0;
|
||||
packet[11] = 0;
|
||||
packet[16] = 0;
|
||||
packet[17] = 0;
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
uint16_t value;
|
||||
value = convert_channel_frsky(i);
|
||||
if(i < 4)
|
||||
{
|
||||
packet[6+i] = value & 0xff;
|
||||
packet[10+(i>>1)] |= ((value >> 8) & 0x0f) << (4 *(i & 0x01));
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[8+i] = value & 0xff;
|
||||
packet[16+((i-4)>>1)] |= ((value >> 8) & 0x0f) << (4 * ((i-4) & 0x01));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FRSKYD_init(void)
|
||||
{
|
||||
//FrskyD init hop
|
||||
if (sub_protocol==DCLONE)
|
||||
Frsky_init_clone();
|
||||
else
|
||||
{
|
||||
for(uint8_t i=0;i<50;i++)
|
||||
{
|
||||
uint8_t freq = (i * 0x1e) % 0xeb;
|
||||
if(i == 3 || i == 23 || i == 47)
|
||||
freq++;
|
||||
if(i > 47)
|
||||
freq=0;
|
||||
hopping_frequency[i]=freq;
|
||||
}
|
||||
rx_tx_addr[1]=1; // keep compatibility with already bound RXs
|
||||
}
|
||||
|
||||
packet_count=0;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
FRSKYD_RF_init();
|
||||
state = FRSKY_BIND;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = FRSKY_BIND_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t FRSKYD_callback(void)
|
||||
{
|
||||
if (state < FRSKY_BIND_DONE)
|
||||
{
|
||||
FRSKYD_build_bind_packet();
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
CC2500_Strobe(CC2500_SFRX);//0x3A
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
if(IS_BIND_DONE)
|
||||
state = FRSKY_BIND_DONE;
|
||||
else
|
||||
state++;
|
||||
return 9000;
|
||||
}
|
||||
if (state == FRSKY_BIND_DONE)
|
||||
{
|
||||
BIND_DONE;
|
||||
FRSKYD_RF_init();
|
||||
counter = 0;
|
||||
state = FRSKY_DATA2;
|
||||
}
|
||||
else
|
||||
if (state == FRSKY_DATA5)
|
||||
{
|
||||
CC2500_Strobe(CC2500_SRX);//0x34 RX enable
|
||||
state = FRSKY_DATA1;
|
||||
return 9200;
|
||||
}
|
||||
counter = (counter + 1) % 188;
|
||||
if (state == FRSKY_DATA4)
|
||||
{ //telemetry receive
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
state++;
|
||||
return 1300;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state == FRSKY_DATA1)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(9000);
|
||||
#endif
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if (len && len<=(0x11+3))// 20bytes
|
||||
{
|
||||
CC2500_ReadData(packet_in, len); //received telemetry packets
|
||||
#if defined(TELEMETRY)
|
||||
if(packet_in[len-1] & 0x80)
|
||||
{//with valid crc
|
||||
packet_count=0;
|
||||
frsky_process_telemetry(packet_in,len); //check if valid telemetry packets and buffer them.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_count++;
|
||||
// restart sequence on missed packet - might need count or timeout instead of one missed
|
||||
if(packet_count>100)
|
||||
{//~1sec
|
||||
packet_count=0;
|
||||
#if defined TELEMETRY
|
||||
telemetry_link=0;//no link frames
|
||||
packet_in[6]=0;//no user frames.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower(); // Set tx_power
|
||||
}
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[counter % 47]);
|
||||
CC2500_SetFreqOffset();
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
FRSKYD_data_frame();
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
state++;
|
||||
}
|
||||
return state == FRSKY_DATA4 ? 7500 : 9000;
|
||||
}
|
||||
#endif
|
||||
257
Multiprotocol/FrSkyL_cc2500.ino
Normal file
257
Multiprotocol/FrSkyL_cc2500.ino
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#if defined(FRSKYL_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define FRSKYL_FORCE_ID
|
||||
#define FRSKYL_PACKET_LEN 256
|
||||
#define FRSKYL_PERIOD 18000
|
||||
|
||||
uint8_t FrSkyL_buffer[FRSKYL_PACKET_LEN];
|
||||
|
||||
static void __attribute__((unused)) FrSkyL_build_bind_packet()
|
||||
{
|
||||
//Header
|
||||
packet[0] = 0x4E; // Unknown but constant
|
||||
//Bind packet
|
||||
memset(&packet[1],0x00,3);
|
||||
//ID
|
||||
packet[4 ] = rx_tx_addr[3]; // ID
|
||||
packet[5 ] = rx_tx_addr[2]; // ID
|
||||
int idx = ((state -FRSKY_BIND) % 10) * 5;
|
||||
packet[6 ] = idx;
|
||||
packet[7 ] = hopping_frequency[idx++];
|
||||
packet[8 ] = hopping_frequency[idx++];
|
||||
packet[9 ] = hopping_frequency[idx++];
|
||||
packet[10] = hopping_frequency[idx++];
|
||||
packet[11] = hopping_frequency[idx++];
|
||||
packet[12] = rx_tx_addr[1]; // ID or hw ver?
|
||||
packet[13] = RX_num;
|
||||
packet[14] = 0x00; // Unknown but constant
|
||||
//CRC
|
||||
uint16_t lcrc = FrSkyX_crc(&packet[1], 14);
|
||||
packet[15] = lcrc >> 8;
|
||||
packet[16] = lcrc;
|
||||
//Debug
|
||||
/* debug("Bind:");
|
||||
for(uint8_t i=0;i<17;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");*/
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyL_build_packet()
|
||||
{
|
||||
static uint8_t chan_offset=0;
|
||||
uint16_t chan_0,chan_1;
|
||||
|
||||
//Header
|
||||
packet[0 ] = 0x4E; // Unknown but constant
|
||||
//ID
|
||||
packet[1 ] = rx_tx_addr[3]; // ID
|
||||
packet[2 ] = rx_tx_addr[2]; // ID
|
||||
packet[3 ] = rx_tx_addr[1]; // ID or hw ver?
|
||||
//skip_hop
|
||||
packet[4 ] = (FrSkyX_chanskip<<6)|hopping_frequency_no;
|
||||
packet[5 ] = FrSkyX_chanskip>>2;
|
||||
//Channels
|
||||
uint8_t startChan = chan_offset;
|
||||
for(uint8_t i = 0; i <9 ; i+=3)
|
||||
{//9 bytes of channel data
|
||||
chan_0 = FrSkyX_scaleForPXX(startChan,6);
|
||||
startChan++;
|
||||
//
|
||||
chan_1 = FrSkyX_scaleForPXX(startChan,6);
|
||||
startChan++;
|
||||
//
|
||||
packet[6+i] = lowByte(chan_0); //3 bytes*4
|
||||
packet[6+i+1]=(((chan_0>>8) & 0x0F)|(chan_1 << 4));
|
||||
packet[6+i+2]=chan_1>>4;
|
||||
}
|
||||
if(sub_protocol & 0x01 ) //6ch mode only??
|
||||
chan_offset = 0 ;
|
||||
else
|
||||
chan_offset^=0x06;
|
||||
//CRC
|
||||
uint16_t lcrc = FrSkyX_crc(&packet[1], 14, RX_num);
|
||||
packet[15] = lcrc >> 8;
|
||||
packet[16] = lcrc;
|
||||
//Debug
|
||||
/*debug("Norm:");
|
||||
for(uint8_t i=0;i<17;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");*/
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyL_encode_packet(bool type)
|
||||
{
|
||||
#define FRSKYL_BIT0 0xED
|
||||
#define FRSKYL_BIT1 0x712
|
||||
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
uint8_t idx = 0,len=6;
|
||||
if(type)
|
||||
{//just replace packet content
|
||||
idx=66;
|
||||
len=17;
|
||||
}
|
||||
|
||||
//debugln("Encode:");
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
uint8_t tmp=packet[i];
|
||||
//debug("%02X =",tmp);
|
||||
for(uint8_t j=0;j<8;j++)
|
||||
{
|
||||
bits <<= 11;
|
||||
if(tmp&0x01)
|
||||
bits |= FRSKYL_BIT1;
|
||||
else
|
||||
bits |= FRSKYL_BIT0;
|
||||
tmp >>=1;
|
||||
bitsavailable += 11;
|
||||
while (bitsavailable >= 8) {
|
||||
uint32_t bits_tmp=bits>>(bitsavailable-8);
|
||||
bitsavailable -= 8;
|
||||
FrSkyL_buffer[idx] = bits_tmp;
|
||||
//debug(" %02X",FrSkyL_buffer[idx]);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
//debugln("");
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t FRSKYL_callback()
|
||||
{
|
||||
static uint8_t written=0, send=0;
|
||||
switch(send)
|
||||
{
|
||||
case 1:
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, FrSkyL_buffer, 64);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
CC2500_Strobe(CC2500_SIDLE); // This cancels the current transmission???
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, FrSkyL_buffer, 64);
|
||||
CC2500_Strobe(CC2500_SFTX); // This just clears what we've written???
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, FrSkyL_buffer, 64);
|
||||
written=64;
|
||||
send++;
|
||||
return 2623;
|
||||
case 2:
|
||||
len=FRSKYL_PACKET_LEN-written;
|
||||
if(len>31)
|
||||
len=31;
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, FrSkyL_buffer+written, len);
|
||||
written+=len;
|
||||
if(len!=31) //everything has been sent
|
||||
{
|
||||
send=0;
|
||||
return 2936;
|
||||
}
|
||||
return 1984;
|
||||
}
|
||||
|
||||
switch(state)
|
||||
{
|
||||
default:
|
||||
//Bind
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(9000);
|
||||
#endif
|
||||
FrSkyX_set_start(47);
|
||||
CC2500_SetPower();
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
//
|
||||
FrSkyL_build_bind_packet();
|
||||
FrSkyL_encode_packet(true);
|
||||
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
if(IS_BIND_DONE)
|
||||
state = FRSKY_BIND_DONE;
|
||||
else
|
||||
{
|
||||
state++;
|
||||
send=1;
|
||||
}
|
||||
return 537;
|
||||
case FRSKY_BIND_DONE:
|
||||
FrSkyX_initialize_data(0);
|
||||
hopping_frequency_no=0;
|
||||
BIND_DONE;
|
||||
state++; //FRSKY_DATA1
|
||||
break;
|
||||
|
||||
case FRSKY_DATA1:
|
||||
CC2500_SetFreqOffset();
|
||||
FrSkyX_set_start(hopping_frequency_no);
|
||||
FrSkyL_build_packet();
|
||||
FrSkyL_encode_packet(true);
|
||||
CC2500_SetPower();
|
||||
hopping_frequency_no = (hopping_frequency_no+FrSkyX_chanskip)%47;
|
||||
send=1;
|
||||
return 537;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FRSKYL_init()
|
||||
{
|
||||
set_rx_tx_addr(MProtocol_id_master);
|
||||
rx_tx_addr[1]=0x02; // ID related, hw version?
|
||||
|
||||
#ifdef FRSKYL_FORCE_ID
|
||||
rx_tx_addr[3]=0x0E;
|
||||
rx_tx_addr[2]=0x1C;
|
||||
rx_tx_addr[1]=0x02;
|
||||
#endif
|
||||
FrSkyX2_init_hop();
|
||||
|
||||
while(!FrSkyX_chanskip)
|
||||
FrSkyX_chanskip=random(0xfefefefe)%47;
|
||||
|
||||
FrSkyX_RF_init();
|
||||
|
||||
//Prepare frame
|
||||
memset(FrSkyL_buffer,0x00,FRSKYL_PACKET_LEN-3);
|
||||
memset(&FrSkyL_buffer[FRSKYL_PACKET_LEN-3],0x55,3);
|
||||
memset(packet,0xAA,6);
|
||||
FrSkyL_encode_packet(false);
|
||||
/*debugln("Frame:");
|
||||
for(uint16_t i=0;i<FRSKYL_PACKET_LEN;i++)
|
||||
{
|
||||
debug(" %02X",FrSkyL_buffer[i]);
|
||||
if(i%11==10)
|
||||
debugln("");
|
||||
}
|
||||
debugln("");*/
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
state = FRSKY_BIND;
|
||||
FrSkyX_initialize_data(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = FRSKY_DATA1;
|
||||
FrSkyX_initialize_data(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
319
Multiprotocol/FrSkyR9_sx1276.ino
Normal file
319
Multiprotocol/FrSkyR9_sx1276.ino
Normal file
@@ -0,0 +1,319 @@
|
||||
#if defined(FRSKYR9_SX1276_INO)
|
||||
#include "iface_sx1276.h"
|
||||
|
||||
#define DISP_FREQ_TABLE
|
||||
|
||||
#define FLEX_FREQ 29
|
||||
#define FCC_FREQ 43
|
||||
#define EU_FREQ 19
|
||||
|
||||
enum {
|
||||
FRSKYR9_FREQ=0,
|
||||
FRSKYR9_DATA,
|
||||
FRSKYR9_RX1,
|
||||
FRSKYR9_RX2,
|
||||
};
|
||||
|
||||
void FrSkyR9_set_frequency()
|
||||
{
|
||||
uint8_t data[3];
|
||||
uint16_t num=0;
|
||||
hopping_frequency_no += FrSkyX_chanskip;
|
||||
switch(sub_protocol & 0xFD)
|
||||
{
|
||||
case R9_868:
|
||||
if(IS_BIND_DONE) // if bind is in progress use R9_915 instead
|
||||
{
|
||||
hopping_frequency_no %= FLEX_FREQ;
|
||||
num=hopping_frequency_no;
|
||||
if(hopping_frequency_no>=FLEX_FREQ-2)
|
||||
num+=FrSkyX_chanskip-FLEX_FREQ+2; // the last 2 values are FrSkyX_chanskip and FrSkyX_chanskip+1
|
||||
num <<= 5;
|
||||
num += 0xD700;
|
||||
break;
|
||||
}//else use R9_915
|
||||
case R9_915:
|
||||
hopping_frequency_no %= FLEX_FREQ;
|
||||
num=hopping_frequency_no;
|
||||
if(hopping_frequency_no>=FLEX_FREQ-2)
|
||||
num+=FrSkyX_chanskip-FLEX_FREQ+2; // the last 2 values are FrSkyX_chanskip and FrSkyX_chanskip+1
|
||||
num <<= 5;
|
||||
num += 0xE4C0;
|
||||
break;
|
||||
case R9_FCC:
|
||||
hopping_frequency_no %= FCC_FREQ;
|
||||
num=hopping_frequency_no;
|
||||
num <<= 5;
|
||||
num += 0xE200;
|
||||
break;
|
||||
case R9_EU:
|
||||
hopping_frequency_no %= EU_FREQ;
|
||||
num=hopping_frequency_no;
|
||||
num <<= 4;
|
||||
num += 0xD7D0;
|
||||
break;
|
||||
}
|
||||
data[0] = num>>8;
|
||||
data[1] = num&0xFF;
|
||||
data[2] = 0x00;
|
||||
|
||||
#ifdef DISP_FREQ_TABLE
|
||||
if(phase==0xFF)
|
||||
debugln("F%d=%02X%02X%02X=%lu", hopping_frequency_no, data[0], data[1], data[2], (uint32_t)((data[0]<<16)+(data[1]<<8)+data[2])*61);
|
||||
#endif
|
||||
SX1276_WriteRegisterMulti(SX1276_06_FRFMSB, data, 3);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyR9_build_packet()
|
||||
{
|
||||
//ID
|
||||
packet[0] = rx_tx_addr[1];
|
||||
packet[1] = rx_tx_addr[2];
|
||||
packet[2] = rx_tx_addr[3];
|
||||
|
||||
//Hopping
|
||||
packet[3] = hopping_frequency_no; // current channel index
|
||||
packet[4] = FrSkyX_chanskip; // step size and last 2 channels start index
|
||||
|
||||
//RX number
|
||||
packet[5] = RX_num; // receiver number from OpenTX
|
||||
|
||||
//Channels
|
||||
FrSkyX_channels(6); // Set packet[6]=failsafe, packet[7]=0?? and packet[8..19]=channels data
|
||||
|
||||
//Bind
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{// 915 0x01=CH1-8_TELEM_ON 0x41=CH1-8_TELEM_OFF 0xC1=CH9-16_TELEM_OFF 0x81=CH9-16_TELEM_ON
|
||||
packet[6] = 0x01; // bind indicator
|
||||
if(sub_protocol & 1)
|
||||
packet[6] |= 0x20; // 868
|
||||
if(binding_idx&0x01)
|
||||
packet[6] |= 0x40; // telem OFF
|
||||
if(binding_idx&0x02)
|
||||
packet[6] |= 0x80; // ch9-16
|
||||
}
|
||||
|
||||
//Sequence and send SPort
|
||||
FrSkyX_seq_sport(20,23); //20=RX|TXseq, 21=bytes count, 22&23=data
|
||||
|
||||
//CRC
|
||||
uint16_t crc = FrSkyX_crc(packet, 24);
|
||||
packet[24] = crc; // low byte
|
||||
packet[25] = crc >> 8; // high byte
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) FrSkyR9_CRC8(uint8_t *p, uint8_t l)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
for (uint8_t i = 0; i < l; i++)
|
||||
{
|
||||
crc = crc ^ p[i];
|
||||
for ( uint8_t j = 0; j < 8; j++ )
|
||||
if ( crc & 0x80 )
|
||||
{
|
||||
crc <<= 1;
|
||||
crc ^= 0x07;
|
||||
}
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyR9_build_EU_packet()
|
||||
{
|
||||
//ID
|
||||
packet[0] = rx_tx_addr[1];
|
||||
packet[1] = rx_tx_addr[2];
|
||||
packet[2] = rx_tx_addr[3];
|
||||
|
||||
//Hopping
|
||||
packet[3] = FrSkyX_chanskip; // step size and last 2 channels start index
|
||||
|
||||
//RX number
|
||||
packet[4] = RX_num; // receiver number from OpenTX
|
||||
|
||||
//Channels
|
||||
//TODO FrSkyX_channels(5,4); // Set packet[5]=failsafe and packet[6..11]=4 channels data
|
||||
|
||||
//Bind
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[5] = 0x01; // bind indicator
|
||||
if((sub_protocol & 2) == 0)
|
||||
packet[5] |= 0x10; // 16CH
|
||||
// if(sub_protocol & 1)
|
||||
// packet[5] |= 0x20; // 868
|
||||
if(binding_idx&0x01)
|
||||
packet[5] |= 0x40; // telem OFF
|
||||
if(binding_idx&0x02)
|
||||
packet[5] |= 0x80; // ch9-16
|
||||
}
|
||||
|
||||
//Sequence and send SPort
|
||||
packet[12] = (FrSkyX_RX_Seq << 4)|0x08; //TX=8 at startup
|
||||
|
||||
//CRC
|
||||
packet[13] = FrSkyR9_CRC8(packet, 13);
|
||||
}
|
||||
|
||||
void FRSKYR9_init()
|
||||
{
|
||||
//Check frequencies
|
||||
#ifdef DISP_FREQ_TABLE
|
||||
phase=0xFF;
|
||||
FrSkyX_chanskip=1;
|
||||
hopping_frequency_no=0xFF;
|
||||
for(uint8_t i=0;i<FCC_FREQ;i++)
|
||||
FrSkyR9_set_frequency();
|
||||
#endif
|
||||
|
||||
//Reset ID
|
||||
set_rx_tx_addr(MProtocol_id_master);
|
||||
|
||||
//FrSkyX_chanskip
|
||||
FrSkyX_chanskip = 1 + (random(0xfefefefe) % 24);
|
||||
debugln("chanskip=%d", FrSkyX_chanskip);
|
||||
|
||||
//Set FrSkyFormat
|
||||
if((sub_protocol & 0x02) == 0)
|
||||
FrSkyFormat=0; // 16 channels
|
||||
else
|
||||
FrSkyFormat=1; // 8 channels
|
||||
debugln("%dCH", FrSkyFormat&1 ? 8:16);
|
||||
|
||||
//EU packet length
|
||||
if( (sub_protocol & 0xFD) == R9_EU )
|
||||
packet_length=14;
|
||||
else
|
||||
packet_length=26;
|
||||
|
||||
//SX1276 Init
|
||||
SX1276_SetMode(true, false, SX1276_OPMODE_SLEEP);
|
||||
SX1276_SetMode(true, false, SX1276_OPMODE_STDBY);
|
||||
|
||||
// uint8_t buffer[2];
|
||||
// buffer[0] = 0x00;
|
||||
// buffer[1] = 0x00;
|
||||
// SX1276_WriteRegisterMulti(SX1276_40_DIOMAPPING1, buffer, 2);
|
||||
|
||||
SX1276_SetDetectOptimize(true, SX1276_DETECT_OPTIMIZE_SF6);
|
||||
SX1276_ConfigModem1(SX1276_MODEM_CONFIG1_BW_500KHZ, SX1276_MODEM_CONFIG1_CODING_RATE_4_5, true);
|
||||
SX1276_ConfigModem2(6, false, false);
|
||||
SX1276_ConfigModem3(false, false);
|
||||
SX1276_SetPreambleLength(9);
|
||||
SX1276_SetDetectionThreshold(SX1276_MODEM_DETECTION_THRESHOLD_SF6);
|
||||
SX1276_SetLna(1, true);
|
||||
SX1276_SetHopPeriod(0); // 0 = disabled, we hop frequencies manually
|
||||
//RF Power
|
||||
SX1276_SetPaDac(false); // Disable 20dBm mode
|
||||
#if MULTI_5IN1_INTERNAL == JP_T18
|
||||
SX1276_SetPaConfig(true, 7, 0); // Lowest power for the T18: 2dBm
|
||||
#else
|
||||
SX1276_SetPaConfig(true, 7, option); // Use PA_HP on PA_BOOST, power=17-(15-option) dBm with option equal or lower to 15
|
||||
#endif
|
||||
SX1276_SetOcp(true,27); // Set OCP to max 240mA
|
||||
SX1276_SetTxRxMode(TX_EN); // Set RF switch to TX
|
||||
//Enable all IRQ flags
|
||||
SX1276_WriteReg(SX1276_11_IRQFLAGSMASK,0x00);
|
||||
FrSkyX_telem_init();
|
||||
|
||||
hopping_frequency_no=0;
|
||||
phase=FRSKYR9_FREQ;
|
||||
}
|
||||
|
||||
uint16_t FRSKYR9_callback()
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case FRSKYR9_FREQ:
|
||||
//Force standby
|
||||
SX1276_SetMode(true, false, SX1276_OPMODE_STDBY);
|
||||
//Set frequency
|
||||
FrSkyR9_set_frequency(); // Set current center frequency
|
||||
//Set power
|
||||
// max power: 15dBm (10.8 + 0.6 * MaxPower [dBm])
|
||||
// output_power: 2 dBm ( (if pa_boost_pin == true))
|
||||
#if MULTI_5IN1_INTERNAL != JP_T18
|
||||
if(option != prev_option)
|
||||
{ // Set RF power if it has changed
|
||||
SX1276_SetPaConfig(true, 7, option); // Use PA_HP on PA_BOOST, power=17-(15-option) dBm with option equal or lower to 15
|
||||
prev_option = option;
|
||||
}
|
||||
#endif
|
||||
//Build packet
|
||||
if( packet_length == 26 )
|
||||
FrSkyR9_build_packet();
|
||||
else
|
||||
FrSkyR9_build_EU_packet();
|
||||
phase++;
|
||||
return 460; // Frequency settle time
|
||||
case FRSKYR9_DATA:
|
||||
//Set RF switch to TX
|
||||
SX1276_SetTxRxMode(TX_EN);
|
||||
//Send packet
|
||||
SX1276_WritePayloadToFifo(packet, packet_length);
|
||||
SX1276_SetMode(true, false, SX1276_OPMODE_TX);
|
||||
#if not defined TELEMETRY
|
||||
phase=FRSKYR9_FREQ;
|
||||
return 20000-460;
|
||||
#else
|
||||
phase++;
|
||||
return 11140; // Packet send time
|
||||
case FRSKYR9_RX1:
|
||||
//Force standby
|
||||
SX1276_SetMode(true, false, SX1276_OPMODE_STDBY);
|
||||
//RX packet size is 13
|
||||
SX1276_WriteReg(SX1276_22_PAYLOAD_LENGTH, 13);
|
||||
//Reset pointer
|
||||
SX1276_WriteReg(SX1276_0D_FIFOADDRPTR, 0x00);
|
||||
//Set RF switch to RX
|
||||
SX1276_SetTxRxMode(RX_EN);
|
||||
//Clear all IRQ flags
|
||||
SX1276_WriteReg(SX1276_12_REGIRQFLAGS,0xFF);
|
||||
//Switch to RX
|
||||
SX1276_WriteReg(SX1276_01_OPMODE, 0x85);
|
||||
phase++;
|
||||
return 7400;
|
||||
case FRSKYR9_RX2:
|
||||
if( (SX1276_ReadReg(SX1276_12_REGIRQFLAGS)&0xF0) == (_BV(SX1276_REGIRQFLAGS_RXDONE) | _BV(SX1276_REGIRQFLAGS_VALIDHEADER)) )
|
||||
{
|
||||
if(SX1276_ReadReg(SX1276_13_REGRXNBBYTES)==13)
|
||||
{
|
||||
SX1276_ReadRegisterMulti(SX1276_00_FIFO,packet_in,13);
|
||||
if( packet_in[9]==rx_tx_addr[1] && packet_in[10]==rx_tx_addr[2] && FrSkyX_crc(packet_in, 11, rx_tx_addr[1]+(rx_tx_addr[2]<<8))==(packet_in[11]+(packet_in[12]<<8)) )
|
||||
{
|
||||
if(packet_in[0]&0x80)
|
||||
RX_RSSI=packet_in[0]<<1;
|
||||
else
|
||||
v_lipo1=(packet_in[0]<<1)+1;
|
||||
//TX_LQI=~(SX1276_ReadReg(SX1276_19_PACKETSNR)>>2)+1;
|
||||
TX_RSSI=SX1276_ReadReg(SX1276_1A_PACKETRSSI)-157;
|
||||
for(uint8_t i=0;i<9;i++)
|
||||
packet[4+i]=packet_in[i]; // Adjust buffer to match FrSkyX
|
||||
frsky_process_telemetry(packet,len); // Process telemetry packet
|
||||
pps_counter++;
|
||||
if(TX_LQI==0)
|
||||
TX_LQI++; // Recover telemetry right away
|
||||
}
|
||||
}
|
||||
}
|
||||
if (millis() - pps_timer >= 1000)
|
||||
{//1 packet every 20ms
|
||||
pps_timer = millis();
|
||||
debugln("%d pps", pps_counter);
|
||||
TX_LQI = pps_counter<<1; // Max=100%
|
||||
pps_counter = 0;
|
||||
}
|
||||
if(TX_LQI==0)
|
||||
FrSkyX_telem_init(); // Reset telemetry
|
||||
else
|
||||
telemetry_link=1; // Send telemetry out anyway
|
||||
phase=FRSKYR9_FREQ;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
|
||||
#endif
|
||||
163
Multiprotocol/FrSkyV_cc2500.ino
Normal file
163
Multiprotocol/FrSkyV_cc2500.ino
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(FRSKYV_CC2500_INO)
|
||||
|
||||
#define FRSKYV_BIND_COUNT 200
|
||||
|
||||
enum {
|
||||
FRSKYV_DATA1=0,
|
||||
FRSKYV_DATA2,
|
||||
FRSKYV_DATA3,
|
||||
FRSKYV_DATA4,
|
||||
FRSKYV_DATA5
|
||||
};
|
||||
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
static uint8_t __attribute__((unused)) FRSKYV_crc8(uint8_t result, uint8_t *data, uint8_t len)
|
||||
{
|
||||
for(uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
result = result ^ data[i];
|
||||
for(uint8_t j = 0; j < 8; j++)
|
||||
if(result & 0x80)
|
||||
result = (result << 1) ^ 0x07;
|
||||
else
|
||||
result = result << 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) FRSKYV_crc8_le(uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t result = 0xD6;
|
||||
|
||||
for(uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
result = result ^ data[i];
|
||||
for(uint8_t j = 0; j < 8; j++)
|
||||
if(result & 0x01)
|
||||
result = (result >> 1) ^ 0x83;
|
||||
else
|
||||
result = result >> 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKYV_build_bind_packet()
|
||||
{
|
||||
//0e 03 01 57 12 00 06 0b 10 15 1a 00 00 00 61
|
||||
packet[0] = 0x0e; //Length
|
||||
packet[1] = 0x03; //Packet type
|
||||
packet[2] = 0x01; //Packet type
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[4] = rx_tx_addr[2];
|
||||
packet[5] = (binding_idx % 10) * 5;
|
||||
packet[6] = packet[5] * 5 + 6;
|
||||
packet[7] = packet[5] * 5 + 11;
|
||||
packet[8] = packet[5] * 5 + 16;
|
||||
packet[9] = packet[5] * 5 + 21;
|
||||
packet[10] = packet[5] * 5 + 26;
|
||||
packet[11] = 0x00;
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = FRSKYV_crc8(0x93, packet, 14);
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) FRSKYV_calc_channel()
|
||||
{
|
||||
uint32_t temp=seed;
|
||||
temp = (temp * 0xaa) % 0x7673;
|
||||
seed = temp;
|
||||
return (seed & 0xff) % 0x32;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKYV_build_data_packet()
|
||||
{
|
||||
uint8_t idx = 0; // transmit lower channels
|
||||
|
||||
packet[0] = 0x0e;
|
||||
packet[1] = rx_tx_addr[3];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = seed & 0xff;
|
||||
packet[4] = seed >> 8;
|
||||
if (phase == FRSKYV_DATA1 || phase == FRSKYV_DATA3)
|
||||
packet[5] = 0x0f;
|
||||
else
|
||||
if(phase == FRSKYV_DATA2 || phase == FRSKYV_DATA4)
|
||||
{
|
||||
packet[5] = 0xf0;
|
||||
idx=4; // transmit upper channels
|
||||
}
|
||||
else
|
||||
packet[5] = 0x00;
|
||||
for(uint8_t i = 0; i < 4; i++)
|
||||
{
|
||||
uint16_t value = convert_channel_frsky(i+idx);
|
||||
packet[2*i + 6] = value & 0xff;
|
||||
packet[2*i + 7] = value >> 8;
|
||||
}
|
||||
packet[14] = FRSKYV_crc8(crc8, packet, 14);
|
||||
}
|
||||
|
||||
uint16_t FRSKYV_callback(void)
|
||||
{
|
||||
if(IS_BIND_DONE)
|
||||
{ // Normal operation
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(9006);
|
||||
#endif
|
||||
uint8_t chan = FRSKYV_calc_channel();
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_SetFreqOffset();
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, chan * 5 + 6);
|
||||
FRSKYV_build_data_packet();
|
||||
|
||||
if (phase == FRSKYV_DATA5)
|
||||
{
|
||||
CC2500_SetPower();
|
||||
phase = FRSKYV_DATA1;
|
||||
}
|
||||
else
|
||||
phase++;
|
||||
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
return 9006;
|
||||
}
|
||||
// Bind mode
|
||||
FRSKYV_build_bind_packet();
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, 0x00);
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
binding_idx++;
|
||||
if(binding_idx>=FRSKYV_BIND_COUNT)
|
||||
BIND_DONE;
|
||||
return 53460;
|
||||
}
|
||||
|
||||
void FRSKYV_init(void)
|
||||
{
|
||||
//ID is 15 bits. Using rx_tx_addr[2] and rx_tx_addr[3] since we want to use RX_Num for model match
|
||||
rx_tx_addr[2]&=0x7F;
|
||||
crc8 = FRSKYV_crc8_le(rx_tx_addr+2, 2);
|
||||
|
||||
FRSKY_init_cc2500(FRSKYV_cc2500_conf);
|
||||
seed = 1;
|
||||
binding_idx=0;
|
||||
phase = FRSKYV_DATA1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,5 +17,269 @@
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_build_bind_packet()
|
||||
{
|
||||
uint8_t packet_size = 0x1D;
|
||||
if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
|
||||
packet_size=0x20; // FrSkyX V1 LBT
|
||||
//Header
|
||||
packet[0] = packet_size; // Number of bytes in the packet (after this one)
|
||||
packet[1] = 0x03; // Bind packet
|
||||
packet[2] = 0x01; // Bind packet
|
||||
|
||||
//ID
|
||||
packet[3] = rx_tx_addr[3]; // ID
|
||||
packet[4] = rx_tx_addr[2]; // ID
|
||||
|
||||
if(protocol==PROTO_FRSKYX)
|
||||
{
|
||||
int idx = ((state -FRSKY_BIND) % 10) * 5;
|
||||
packet[5] = idx;
|
||||
packet[6] = hopping_frequency[idx++];
|
||||
packet[7] = hopping_frequency[idx++];
|
||||
packet[8] = hopping_frequency[idx++];
|
||||
packet[9] = hopping_frequency[idx++];
|
||||
packet[10] = hopping_frequency[idx++];
|
||||
packet[11] = rx_tx_addr[1]; // Unknown but constant ID?
|
||||
packet[12] = RX_num;
|
||||
//
|
||||
memset(&packet[13], 0, packet_size - 14);
|
||||
if(binding_idx&0x01)
|
||||
memcpy(&packet[13],(void *)"\x55\xAA\x5A\xA5",4); // Telem off
|
||||
if(binding_idx&0x02)
|
||||
memcpy(&packet[17],(void *)"\x55\xAA\x5A\xA5",4); // CH9-16
|
||||
}
|
||||
else
|
||||
{
|
||||
//packet 1D 03 01 0E 1C 02 00 00 32 0B 00 00 A8 26 28 01 A1 00 00 00 3E F6 87 C7 00 00 00 00 C9 C9
|
||||
packet[5] = rx_tx_addr[1]; // Unknown but constant ID?
|
||||
packet[6] = RX_num;
|
||||
//Bind flags
|
||||
packet[7]=0;
|
||||
if(binding_idx&0x01)
|
||||
packet[7] |= 0x40; // Telem off
|
||||
if(binding_idx&0x02)
|
||||
packet[7] |= 0x80; // CH9-16
|
||||
//Unknown bytes
|
||||
memcpy(&packet[8],"\x32\x0B\x00\x00\xA8\x26\x28\x01\xA1\x00\x00\x00\x3E\xF6\x87\xC7",16);
|
||||
packet[20]^= 0x0E ^ rx_tx_addr[3]; // Update the ID
|
||||
packet[21]^= 0x1C ^ rx_tx_addr[2]; // Update the ID
|
||||
//Xor
|
||||
for(uint8_t i=3; i<packet_size-1; i++)
|
||||
packet[i] ^= 0xA7;
|
||||
}
|
||||
//CRC
|
||||
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_size-4);
|
||||
packet[packet_size-1] = lcrc >> 8;
|
||||
packet[packet_size] = lcrc;
|
||||
|
||||
/*//Debug
|
||||
debug("Bind:");
|
||||
for(uint8_t i=0;i<=packet_size;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");*/
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FrSkyX_build_packet()
|
||||
{
|
||||
//0x1D 0xB3 0xFD 0x02 0x56 0x07 0x15 0x00 0x00 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x96 0x12
|
||||
// data frames sent every 9ms; failsafe every 9 seconds
|
||||
//
|
||||
uint8_t packet_size = 0x1D;
|
||||
if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
|
||||
packet_size=0x20; // FrSkyX V1 LBT
|
||||
//Header
|
||||
packet[0] = packet_size; // Number of bytes in the packet (after this one)
|
||||
packet[1] = rx_tx_addr[3]; // ID
|
||||
packet[2] = rx_tx_addr[2]; // ID
|
||||
packet[3] = rx_tx_addr[1]; // Unknown but constant ID?
|
||||
//
|
||||
packet[4] = (FrSkyX_chanskip<<6)|hopping_frequency_no;
|
||||
packet[5] = FrSkyX_chanskip>>2;
|
||||
packet[6] = RX_num;
|
||||
|
||||
//Channels
|
||||
FrSkyX_channels(7); // Set packet[7]=failsafe, packet[8]=0?? and packet[9..20]=channels data
|
||||
|
||||
//Sequence and send SPort
|
||||
FrSkyX_seq_sport(21,packet_size-2); //21=RX|TXseq, 22=bytes count, 23..packet_size-2=data
|
||||
|
||||
//CRC
|
||||
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_size-4);
|
||||
packet[packet_size-1] = lcrc >> 8;
|
||||
packet[packet_size] = lcrc;
|
||||
|
||||
/*//Debug
|
||||
debug("Norm:");
|
||||
for(uint8_t i=0;i<=packet_size;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");*/
|
||||
}
|
||||
|
||||
uint16_t FRSKYX_callback()
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
default:
|
||||
FrSkyX_set_start(47);
|
||||
CC2500_SetPower();
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
//
|
||||
FrSkyX_build_bind_packet();
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
if(IS_BIND_DONE)
|
||||
state = FRSKY_BIND_DONE;
|
||||
else
|
||||
state++;
|
||||
return 9000;
|
||||
case FRSKY_BIND_DONE:
|
||||
FrSkyX_initialize_data(0);
|
||||
hopping_frequency_no=0;
|
||||
BIND_DONE;
|
||||
state++; //FRSKY_DATA1
|
||||
break;
|
||||
|
||||
case FRSKY_DATA1:
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_SetFreqOffset();
|
||||
FrSkyX_set_start(hopping_frequency_no);
|
||||
FrSkyX_build_packet();
|
||||
if(FrSkyFormat & 2)
|
||||
{// LBT
|
||||
CC2500_Strobe(CC2500_SRX); //Acquire RSSI
|
||||
state++;
|
||||
return 400; // LBT
|
||||
}
|
||||
case FRSKY_DATA2:
|
||||
if(FrSkyFormat & 2)
|
||||
{
|
||||
uint16_t rssi=0;
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
rssi += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST); // 0.5 db/count, RSSI value read from the RSSI status register is a 2's complement number
|
||||
rssi>>=2;
|
||||
#if 0
|
||||
uint8_t rssi_level=convert_channel_8b(CH16)>>1; //CH16 0..127
|
||||
if ( rssi > rssi_level && rssi < 128) //test rssi level dynamically
|
||||
#else
|
||||
if ( rssi > 14 && rssi < 128) //if RSSI above -65dBm (12=-70) => ETSI requirement
|
||||
#endif
|
||||
{
|
||||
LBT_POWER_on; //Reduce to low power before transmitting
|
||||
debugln("Busy %d %d",hopping_frequency_no,rssi);
|
||||
}
|
||||
}
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFTX); //Flush the TXFIFO
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
hopping_frequency_no = (hopping_frequency_no+FrSkyX_chanskip)%47;
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
state=FRSKY_DATA3;
|
||||
if(FrSkyFormat & 2)
|
||||
return 4000; // LBT
|
||||
else
|
||||
return 5200; // FCC
|
||||
case FRSKY_DATA3:
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFRX); //Flush the RXFIFO
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
state++;
|
||||
if(FrSkyFormat & 2)
|
||||
return 4200; // LBT
|
||||
else
|
||||
return 3400; // FCC
|
||||
case FRSKY_DATA4:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(9000);
|
||||
#endif
|
||||
#if defined TELEMETRY
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if (len && len <= 17) //Telemetry frame is 17 bytes
|
||||
{
|
||||
//debug("Telem:");
|
||||
CC2500_ReadData(packet_in, len); //Read what has been received so far
|
||||
if(len<17)
|
||||
{//not all bytes were received
|
||||
uint8_t last_len=CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if(last_len==17) //All bytes received
|
||||
{
|
||||
CC2500_ReadData(packet_in+len, last_len-len); //Finish to read
|
||||
len=17;
|
||||
}
|
||||
}
|
||||
if(len==17 && (protocol==PROTO_FRSKYX || (protocol==PROTO_FRSKYX2 && (packet_in[len-1] & 0x80))) )
|
||||
{//Telemetry received with valid crc for FRSKYX2
|
||||
//Debug
|
||||
//for(uint8_t i=0;i<len;i++)
|
||||
// debug(" %02X",packet_in[i]);
|
||||
if(frsky_process_telemetry(packet_in,len)) //Check and process telemetry packet
|
||||
{//good packet received
|
||||
pps_counter++;
|
||||
if(TX_LQI==0)
|
||||
TX_LQI++; //Recover telemetry right away
|
||||
}
|
||||
}
|
||||
//debugln("");
|
||||
}
|
||||
if (millis() - pps_timer >= 900)
|
||||
{//1 packet every 9ms
|
||||
pps_timer = millis();
|
||||
debugln("%d pps", pps_counter);
|
||||
TX_LQI = pps_counter; //Max=100%
|
||||
pps_counter = 0;
|
||||
}
|
||||
if(TX_LQI==0)
|
||||
FrSkyX_telem_init(); //Reset telemetry
|
||||
else
|
||||
telemetry_link=1; //Send telemetry out anyway
|
||||
#endif
|
||||
state = FRSKY_DATA1;
|
||||
return 400; // FCC & LBT
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FRSKYX_init()
|
||||
{
|
||||
set_rx_tx_addr(MProtocol_id_master);
|
||||
FrSkyFormat = sub_protocol;
|
||||
|
||||
if (sub_protocol==XCLONE_16||sub_protocol==XCLONE_8)
|
||||
Frsky_init_clone();
|
||||
else if(protocol==PROTO_FRSKYX)
|
||||
{
|
||||
Frsky_init_hop();
|
||||
rx_tx_addr[1]=0x02; // ID related, hw version?
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef FRSKYX2_FORCE_ID
|
||||
rx_tx_addr[3]=0x0E;
|
||||
rx_tx_addr[2]=0x1C;
|
||||
FrSkyX_chanskip=18;
|
||||
#endif
|
||||
rx_tx_addr[1]=0x02; // ID related, hw version?
|
||||
FrSkyX2_init_hop();
|
||||
}
|
||||
|
||||
packet_count=0;
|
||||
while(!FrSkyX_chanskip)
|
||||
FrSkyX_chanskip=random(0xfefefefe)%47;
|
||||
|
||||
FrSkyX_RF_init();
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
state = FRSKY_BIND;
|
||||
FrSkyX_initialize_data(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
state = FRSKY_DATA1;
|
||||
FrSkyX_initialize_data(0);
|
||||
}
|
||||
FrSkyX_telem_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
621
Multiprotocol/FrSky_Rx_cc2500.ino
Normal file
621
Multiprotocol/FrSky_Rx_cc2500.ino
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(FRSKY_RX_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
#define FRSKY_RX_D16FCC_LENGTH 0x1D+1
|
||||
#define FRSKY_RX_D16LBT_LENGTH 0x20+1
|
||||
#define FRSKY_RX_D16v2_LENGTH 0x1D+1
|
||||
#define FRSKY_RX_D8_LENGTH 0x11+1
|
||||
#define FRSKY_RX_FORMATS 5
|
||||
|
||||
enum
|
||||
{
|
||||
FRSKY_RX_D8 =0,
|
||||
FRSKY_RX_D16FCC =1,
|
||||
FRSKY_RX_D16LBT =2,
|
||||
FRSKY_RX_D16v2FCC =3,
|
||||
FRSKY_RX_D16v2LBT =4,
|
||||
};
|
||||
|
||||
enum {
|
||||
FRSKY_RX_TUNE_START,
|
||||
FRSKY_RX_TUNE_LOW,
|
||||
FRSKY_RX_TUNE_HIGH,
|
||||
FRSKY_RX_BIND,
|
||||
FRSKY_RX_DATA,
|
||||
};
|
||||
|
||||
const PROGMEM uint8_t FRSKY_RX_common_reg[][2] = {
|
||||
{CC2500_02_IOCFG0, 0x01},
|
||||
{CC2500_18_MCSM0, 0x18},
|
||||
{CC2500_07_PKTCTRL1, 0x05},
|
||||
{CC2500_3E_PATABLE, 0xFF},
|
||||
{CC2500_0C_FSCTRL0, 0},
|
||||
{CC2500_0D_FREQ2, 0x5C},
|
||||
{CC2500_13_MDMCFG1, 0x23},
|
||||
{CC2500_14_MDMCFG0, 0x7A},
|
||||
{CC2500_19_FOCCFG, 0x16},
|
||||
{CC2500_1A_BSCFG, 0x6C},
|
||||
{CC2500_1B_AGCCTRL2, 0x03},
|
||||
{CC2500_1C_AGCCTRL1, 0x40},
|
||||
{CC2500_1D_AGCCTRL0, 0x91},
|
||||
{CC2500_21_FREND1, 0x56},
|
||||
{CC2500_22_FREND0, 0x10},
|
||||
{CC2500_23_FSCAL3, 0xA9},
|
||||
{CC2500_24_FSCAL2, 0x0A},
|
||||
{CC2500_25_FSCAL1, 0x00},
|
||||
{CC2500_26_FSCAL0, 0x11},
|
||||
{CC2500_29_FSTEST, 0x59},
|
||||
{CC2500_2C_TEST2, 0x88},
|
||||
{CC2500_2D_TEST1, 0x31},
|
||||
{CC2500_2E_TEST0, 0x0B},
|
||||
{CC2500_03_FIFOTHR, 0x07},
|
||||
{CC2500_09_ADDR, 0x03},
|
||||
};
|
||||
|
||||
const PROGMEM uint8_t FRSKY_RX_d16fcc_reg[][2] = {
|
||||
{CC2500_17_MCSM1, 0x0C},
|
||||
{CC2500_0E_FREQ1, 0x76},
|
||||
{CC2500_0F_FREQ0, 0x27},
|
||||
{CC2500_06_PKTLEN, 0x1E},
|
||||
{CC2500_08_PKTCTRL0, 0x01},
|
||||
{CC2500_0B_FSCTRL1, 0x0A},
|
||||
{CC2500_10_MDMCFG4, 0x7B},
|
||||
{CC2500_11_MDMCFG3, 0x61},
|
||||
{CC2500_12_MDMCFG2, 0x13},
|
||||
{CC2500_15_DEVIATN, 0x51},
|
||||
};
|
||||
|
||||
const PROGMEM uint8_t FRSKY_RX_d16lbt_reg[][2] = {
|
||||
{CC2500_17_MCSM1, 0x0E},
|
||||
{CC2500_0E_FREQ1, 0x80},
|
||||
{CC2500_0F_FREQ0, 0x00},
|
||||
{CC2500_06_PKTLEN, 0x23},
|
||||
{CC2500_08_PKTCTRL0, 0x01},
|
||||
{CC2500_0B_FSCTRL1, 0x08},
|
||||
{CC2500_10_MDMCFG4, 0x7B},
|
||||
{CC2500_11_MDMCFG3, 0xF8},
|
||||
{CC2500_12_MDMCFG2, 0x03},
|
||||
{CC2500_15_DEVIATN, 0x53},
|
||||
};
|
||||
|
||||
const PROGMEM uint8_t FRSKY_RX_d8_reg[][2] = {
|
||||
{CC2500_17_MCSM1, 0x0C},
|
||||
{CC2500_0E_FREQ1, 0x76},
|
||||
{CC2500_0F_FREQ0, 0x27},
|
||||
{CC2500_06_PKTLEN, 0x19},
|
||||
{CC2500_08_PKTCTRL0, 0x05},
|
||||
{CC2500_0B_FSCTRL1, 0x08},
|
||||
{CC2500_10_MDMCFG4, 0xAA},
|
||||
{CC2500_11_MDMCFG3, 0x39},
|
||||
{CC2500_12_MDMCFG2, 0x11},
|
||||
{CC2500_15_DEVIATN, 0x42},
|
||||
};
|
||||
|
||||
static uint8_t FRSKY_RX_chanskip;
|
||||
static int8_t FRSKY_RX_finetune;
|
||||
static uint8_t FRSKY_RX_format;
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_strobe_rx()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_initialise_cc2500() {
|
||||
const uint8_t FRSKY_RX_length[] = { FRSKY_RX_D8_LENGTH, FRSKY_RX_D16FCC_LENGTH, FRSKY_RX_D16LBT_LENGTH, FRSKY_RX_D16v2_LENGTH, FRSKY_RX_D16v2_LENGTH };
|
||||
packet_length = FRSKY_RX_length[FRSKY_RX_format];
|
||||
CC2500_Reset();
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
for (uint8_t i = 0; i < sizeof(FRSKY_RX_common_reg) / 2; i++)
|
||||
CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_common_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_common_reg[i][1]));
|
||||
|
||||
switch (FRSKY_RX_format)
|
||||
{
|
||||
case FRSKY_RX_D16v2FCC:
|
||||
case FRSKY_RX_D16FCC:
|
||||
for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16fcc_reg) / 2; i++)
|
||||
CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16fcc_reg[i][1]));
|
||||
if(FRSKY_RX_format==FRSKY_RX_D16v2FCC)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC
|
||||
CC2500_WriteReg(CC2500_17_MCSM1, 0x0E); // Go/Stay in RX mode
|
||||
CC2500_WriteReg(CC2500_11_MDMCFG3, 0x84); // bitrate 70K->77K
|
||||
}
|
||||
break;
|
||||
case FRSKY_RX_D16v2LBT:
|
||||
case FRSKY_RX_D16LBT:
|
||||
for (uint8_t i = 0; i < sizeof(FRSKY_RX_d16lbt_reg) / 2; i++)
|
||||
CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d16lbt_reg[i][1]));
|
||||
if(FRSKY_RX_format==FRSKY_RX_D16v2LBT)
|
||||
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x05); // Enable CRC
|
||||
break;
|
||||
case FRSKY_RX_D8:
|
||||
for (uint8_t i = 0; i < sizeof(FRSKY_RX_d8_reg) / 2; i++)
|
||||
CC2500_WriteReg(pgm_read_byte_near(&FRSKY_RX_d8_reg[i][0]), pgm_read_byte_near(&FRSKY_RX_d8_reg[i][1]));
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
break;
|
||||
}
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, 0); // bind channel
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN); // lna disable / enable
|
||||
FRSKY_RX_strobe_rx();
|
||||
delayMicroseconds(1000); // wait for RX to activate
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_set_channel(uint8_t channel)
|
||||
{
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[channel]);
|
||||
if(FRSKY_RX_format == FRSKY_RX_D8)
|
||||
CC2500_WriteReg(CC2500_23_FSCAL3, 0x89);
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[channel]);
|
||||
FRSKY_RX_strobe_rx();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_calibrate()
|
||||
{
|
||||
FRSKY_RX_strobe_rx();
|
||||
for (unsigned c = 0; c < 47; c++)
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[c]);
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
delayMicroseconds(900);
|
||||
calData[c] = CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((unused)) frskyx_rx_check_crc_id(bool bind,bool init)
|
||||
{
|
||||
/*debugln("RX");
|
||||
for(uint8_t i=0; i<packet_length;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");*/
|
||||
|
||||
if(bind && packet[0]!=packet_length-1 && packet[1] !=0x03 && packet[2] != 0x01)
|
||||
return false;
|
||||
uint8_t offset=bind?3:1;
|
||||
|
||||
// Check D8 checksum
|
||||
if (FRSKY_RX_format == FRSKY_RX_D8)
|
||||
{
|
||||
if((packet[packet_length+1] & 0x80) != 0x80) // Check CRC_OK flag in status byte 2
|
||||
return false; // Bad CRC
|
||||
if(init)
|
||||
{//Save TXID
|
||||
rx_tx_addr[3] = packet[3];
|
||||
rx_tx_addr[2] = packet[4];
|
||||
rx_tx_addr[1] = packet[17];
|
||||
}
|
||||
else
|
||||
if(rx_tx_addr[3] != packet[offset] || rx_tx_addr[2] != packet[offset+1] || rx_tx_addr[1] != packet[bind?17:5])
|
||||
return false; // Bad address
|
||||
return true; // Full match
|
||||
}
|
||||
|
||||
// Check D16v2 checksum
|
||||
if (FRSKY_RX_format == FRSKY_RX_D16v2LBT || FRSKY_RX_format == FRSKY_RX_D16v2FCC)
|
||||
if((packet[packet_length+1] & 0x80) != 0x80) // Check CRC_OK flag in status byte 2
|
||||
return false;
|
||||
//debugln("HW Checksum ok");
|
||||
|
||||
// Check D16 checksum
|
||||
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length - 5); // Compute crc
|
||||
uint16_t rcrc = (packet[packet_length-2] << 8) | (packet[packet_length-1] & 0xff); // Received crc
|
||||
if(lcrc != rcrc)
|
||||
return false; // Bad CRC
|
||||
//debugln("Checksum ok");
|
||||
|
||||
if (bind && (FRSKY_RX_format == FRSKY_RX_D16v2LBT || FRSKY_RX_format == FRSKY_RX_D16v2FCC))
|
||||
for(uint8_t i=3; i<packet_length-2; i++) //unXOR bind packet
|
||||
packet[i] ^= 0xA7;
|
||||
|
||||
uint8_t offset2=0;
|
||||
if (bind && (FRSKY_RX_format == FRSKY_RX_D16LBT || FRSKY_RX_format == FRSKY_RX_D16FCC))
|
||||
offset2=6;
|
||||
if(init)
|
||||
{//Save TXID
|
||||
rx_tx_addr[3] = packet[3];
|
||||
rx_tx_addr[2] = packet[4];
|
||||
rx_tx_addr[1] = packet[5+offset2];
|
||||
rx_tx_addr[0] = packet[6+offset2]; // RXnum
|
||||
}
|
||||
else
|
||||
if(rx_tx_addr[3] != packet[offset] || rx_tx_addr[2] != packet[offset+1] || rx_tx_addr[1] != packet[offset+2+offset2])
|
||||
return false; // Bad address
|
||||
//debugln("Address ok");
|
||||
|
||||
if(!bind && rx_tx_addr[0] != packet[6])
|
||||
return false; // Bad RX num
|
||||
|
||||
//debugln("Match");
|
||||
return true; // Full match
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_build_telemetry_packet()
|
||||
{
|
||||
uint16_t raw_channel[8];
|
||||
uint32_t bits = 0;
|
||||
uint8_t bitsavailable = 0;
|
||||
uint8_t idx = 0;
|
||||
uint8_t i;
|
||||
|
||||
if (FRSKY_RX_format == FRSKY_RX_D8)
|
||||
{// decode D8 channels
|
||||
raw_channel[0] = ((packet[10] & 0x0F) << 8 | packet[6]);
|
||||
raw_channel[1] = ((packet[10] & 0xF0) << 4 | packet[7]);
|
||||
raw_channel[2] = ((packet[11] & 0x0F) << 8 | packet[8]);
|
||||
raw_channel[3] = ((packet[11] & 0xF0) << 4 | packet[9]);
|
||||
raw_channel[4] = ((packet[16] & 0x0F) << 8 | packet[12]);
|
||||
raw_channel[5] = ((packet[16] & 0xF0) << 4 | packet[13]);
|
||||
raw_channel[6] = ((packet[17] & 0x0F) << 8 | packet[14]);
|
||||
raw_channel[7] = ((packet[17] & 0xF0) << 4 | packet[15]);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (raw_channel[i] < 1290)
|
||||
raw_channel[i] = 1290;
|
||||
rx_rc_chan[i] = min(((raw_channel[i] - 1290) << 4) / 15, 2047);
|
||||
}
|
||||
}
|
||||
else
|
||||
{// decode D16 channels
|
||||
raw_channel[0] = ((packet[10] << 8) & 0xF00) | packet[9];
|
||||
raw_channel[1] = ((packet[11] << 4) & 0xFF0) | (packet[10] >> 4);
|
||||
raw_channel[2] = ((packet[13] << 8) & 0xF00) | packet[12];
|
||||
raw_channel[3] = ((packet[14] << 4) & 0xFF0) | (packet[13] >> 4);
|
||||
raw_channel[4] = ((packet[16] << 8) & 0xF00) | packet[15];
|
||||
raw_channel[5] = ((packet[17] << 4) & 0xFF0) | (packet[16] >> 4);
|
||||
raw_channel[6] = ((packet[19] << 8) & 0xF00) | packet[18];
|
||||
raw_channel[7] = ((packet[20] << 4) & 0xFF0) | (packet[19] >> 4);
|
||||
for (i = 0; i < 8; i++) {
|
||||
// ignore failsafe channels
|
||||
if(packet[7] != 0x10+(i<<1)) {
|
||||
uint8_t shifted = (raw_channel[i] & 0x800)>0;
|
||||
uint16_t channel_value = raw_channel[i] & 0x7FF;
|
||||
if (channel_value < 64)
|
||||
rx_rc_chan[shifted ? i + 8 : i] = 0;
|
||||
else
|
||||
rx_rc_chan[shifted ? i + 8 : i] = min(((channel_value - 64) << 4) / 15, 2047);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// buid telemetry packet
|
||||
packet_in[idx++] = RX_LQI;
|
||||
packet_in[idx++] = RX_RSSI;
|
||||
packet_in[idx++] = 0; // start channel
|
||||
packet_in[idx++] = FRSKY_RX_format == FRSKY_RX_D8 ? 8 : 16; // number of channels in packet
|
||||
|
||||
// pack channels
|
||||
for (i = 0; i < packet_in[3]; i++) {
|
||||
bits |= ((uint32_t)rx_rc_chan[i]) << bitsavailable;
|
||||
bitsavailable += 11;
|
||||
while (bitsavailable >= 8) {
|
||||
packet_in[idx++] = bits & 0xff;
|
||||
bits >>= 8;
|
||||
bitsavailable -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) FRSKY_RX_data()
|
||||
{
|
||||
uint16_t temp = FRSKY_RX_EEPROM_OFFSET;
|
||||
FRSKY_RX_format = eeprom_read_byte((EE_ADDR)temp++) % FRSKY_RX_FORMATS;
|
||||
rx_tx_addr[3] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
rx_tx_addr[2] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
rx_tx_addr[1] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
rx_tx_addr[0] = RX_num;
|
||||
FRSKY_RX_finetune = eeprom_read_byte((EE_ADDR)temp++);
|
||||
debug("format=%d, ", FRSKY_RX_format);
|
||||
debug("addr[3]=%02X, ", rx_tx_addr[3]);
|
||||
debug("addr[2]=%02X, ", rx_tx_addr[2]);
|
||||
debug("addr[1]=%02X, ", rx_tx_addr[1]);
|
||||
debug("rx_num=%02X, ", rx_tx_addr[0]);
|
||||
debugln("tune=%d", (int8_t)FRSKY_RX_finetune);
|
||||
if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC)
|
||||
{//D8 & D16v1
|
||||
for (uint8_t ch = 0; ch < 47; ch++)
|
||||
hopping_frequency[ch] = eeprom_read_byte((EE_ADDR)temp++);
|
||||
}
|
||||
else
|
||||
{
|
||||
FrSkyFormat=FRSKY_RX_format == FRSKY_RX_D16v2FCC?0:2;
|
||||
FrSkyX2_init_hop();
|
||||
}
|
||||
debug("ch:");
|
||||
for (uint8_t ch = 0; ch < 47; ch++)
|
||||
debug(" %02X", hopping_frequency[ch]);
|
||||
debugln("");
|
||||
|
||||
FRSKY_RX_initialise_cc2500();
|
||||
FRSKY_RX_calibrate();
|
||||
CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // FS_AUTOCAL = manual
|
||||
CC2500_WriteReg(CC2500_09_ADDR, rx_tx_addr[3]); // set address
|
||||
CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); // check address
|
||||
if (option == 0)
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
else
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
FRSKY_RX_set_channel(hopping_frequency_no);
|
||||
phase = FRSKY_RX_DATA;
|
||||
}
|
||||
|
||||
void FRSKY_RX_init()
|
||||
{
|
||||
if(sub_protocol == FRSKY_ERASE)
|
||||
{
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{// Clear all cloned addresses
|
||||
uint16_t addr[]={ FRSKYD_CLONE_EEPROM_OFFSET+1, FRSKYX_CLONE_EEPROM_OFFSET+1, FRSKYX2_CLONE_EEPROM_OFFSET+1 };
|
||||
for(uint8_t i=0; i<3;i++)
|
||||
for(uint8_t j=0; j<3;j++)
|
||||
eeprom_write_byte((EE_ADDR)(addr[i]+j), 0xFF);
|
||||
packet_count = 100;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FRSKY_RX_chanskip = 1;
|
||||
hopping_frequency_no = 0;
|
||||
rx_data_started = false;
|
||||
FRSKY_RX_finetune = 0;
|
||||
telemetry_link = 0;
|
||||
packet_count = 0;
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
FRSKY_RX_format = FRSKY_RX_D8;
|
||||
FRSKY_RX_initialise_cc2500();
|
||||
phase = FRSKY_RX_TUNE_START;
|
||||
debugln("FRSKY_RX_TUNE_START");
|
||||
}
|
||||
else
|
||||
FRSKY_RX_data();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t FRSKY_RX_callback()
|
||||
{
|
||||
static int8_t read_retry = 0;
|
||||
static int8_t tune_low, tune_high;
|
||||
uint8_t len, ch;
|
||||
|
||||
if(sub_protocol == FRSKY_ERASE)
|
||||
{
|
||||
if(packet_count)
|
||||
packet_count--;
|
||||
else
|
||||
BIND_DONE;
|
||||
return 10000; // Nothing to do...
|
||||
}
|
||||
|
||||
if(IS_BIND_DONE && phase != FRSKY_RX_DATA)
|
||||
FRSKY_RX_init(); // Abort bind
|
||||
|
||||
if ((prev_option != option) && (phase >= FRSKY_RX_DATA))
|
||||
{
|
||||
if (option == 0)
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
else
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
prev_option = option;
|
||||
}
|
||||
|
||||
if (rx_disable_lna != IS_POWER_FLAG_on)
|
||||
{
|
||||
rx_disable_lna = IS_POWER_FLAG_on;
|
||||
CC2500_SetTxRxMode(rx_disable_lna ? TXRX_OFF : RX_EN);
|
||||
}
|
||||
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
switch(phase)
|
||||
{
|
||||
case FRSKY_RX_TUNE_START:
|
||||
if (len == packet_length + 2) //+2=RSSI+LQI+CRC
|
||||
{
|
||||
CC2500_ReadData(packet, len);
|
||||
if(frskyx_rx_check_crc_id(true,true))
|
||||
{
|
||||
FRSKY_RX_finetune = -127;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
phase = FRSKY_RX_TUNE_LOW;
|
||||
debugln("FRSKY_RX_TUNE_LOW");
|
||||
FRSKY_RX_strobe_rx();
|
||||
state = 0;
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
FRSKY_RX_format = (FRSKY_RX_format + 1) % FRSKY_RX_FORMATS; // switch to next format (D8, D16FCC, D16LBT, D16v2FCC, D16v2LBT)
|
||||
FRSKY_RX_initialise_cc2500();
|
||||
FRSKY_RX_finetune += 10;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
FRSKY_RX_strobe_rx();
|
||||
return 18000;
|
||||
|
||||
case FRSKY_RX_TUNE_LOW:
|
||||
if (len == packet_length + 2) //+2=RSSI+LQI+CRC
|
||||
{
|
||||
CC2500_ReadData(packet, len);
|
||||
if(frskyx_rx_check_crc_id(true,false)) {
|
||||
tune_low = FRSKY_RX_finetune;
|
||||
FRSKY_RX_finetune = 127;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
phase = FRSKY_RX_TUNE_HIGH;
|
||||
debugln("FRSKY_RX_TUNE_HIGH");
|
||||
FRSKY_RX_strobe_rx();
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
FRSKY_RX_finetune += 1;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
FRSKY_RX_strobe_rx();
|
||||
return 18000;
|
||||
|
||||
case FRSKY_RX_TUNE_HIGH:
|
||||
if (len == packet_length + 2) //+2=RSSI+LQI+CRC
|
||||
{
|
||||
CC2500_ReadData(packet, len);
|
||||
if(frskyx_rx_check_crc_id(true,false)) {
|
||||
tune_high = FRSKY_RX_finetune;
|
||||
FRSKY_RX_finetune = (tune_low + tune_high) / 2;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, (int8_t)FRSKY_RX_finetune);
|
||||
if(tune_low < tune_high)
|
||||
{
|
||||
phase = FRSKY_RX_BIND;
|
||||
debugln("FRSKY_RX_TUNE_HIGH");
|
||||
}
|
||||
else
|
||||
{
|
||||
phase = FRSKY_RX_TUNE_START;
|
||||
debugln("FRSKY_RX_TUNE_START");
|
||||
}
|
||||
FRSKY_RX_strobe_rx();
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
FRSKY_RX_finetune -= 1;
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, FRSKY_RX_finetune);
|
||||
FRSKY_RX_strobe_rx();
|
||||
return 18000;
|
||||
|
||||
case FRSKY_RX_BIND:
|
||||
if (len == packet_length + 2) //+2=RSSI+LQI+CRC
|
||||
{
|
||||
CC2500_ReadData(packet, len);
|
||||
if(frskyx_rx_check_crc_id(true,false)) {
|
||||
if(FRSKY_RX_format != FRSKY_RX_D16v2LBT && FRSKY_RX_format != FRSKY_RX_D16v2FCC)
|
||||
{// D8 & D16v1
|
||||
if(packet[5] <= 0x2D)
|
||||
{
|
||||
for (ch = 0; ch < 5; ch++)
|
||||
hopping_frequency[packet[5]+ch] = packet[6+ch];
|
||||
state |= 1 << (packet[5] / 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
state = 0x3FF; //No hop table for D16v2
|
||||
if (state == 0x3FF)
|
||||
{
|
||||
debugln("Bind complete");
|
||||
BIND_DONE;
|
||||
// store format, finetune setting, txid, channel list
|
||||
uint16_t temp = FRSKY_RX_EEPROM_OFFSET;
|
||||
if(sub_protocol==FRSKY_CLONE)
|
||||
{
|
||||
if(FRSKY_RX_format==FRSKY_RX_D8)
|
||||
temp=FRSKYD_CLONE_EEPROM_OFFSET;
|
||||
else if(FRSKY_RX_format == FRSKY_RX_D16FCC || FRSKY_RX_format == FRSKY_RX_D16LBT)
|
||||
temp=FRSKYX_CLONE_EEPROM_OFFSET;
|
||||
else
|
||||
temp=FRSKYX2_CLONE_EEPROM_OFFSET;
|
||||
}
|
||||
eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_format);
|
||||
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[3]);
|
||||
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[2]);
|
||||
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[1]);
|
||||
if(sub_protocol == FRSKY_RX || sub_protocol == FRSKY_CPPM) // FRSKY_RX, FRSKY_CPPM
|
||||
eeprom_write_byte((EE_ADDR)temp++, FRSKY_RX_finetune);
|
||||
if(FRSKY_RX_format != FRSKY_RX_D16v2FCC && FRSKY_RX_format != FRSKY_RX_D16v2LBT)
|
||||
for (ch = 0; ch < 47; ch++)
|
||||
eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[ch]);
|
||||
FRSKY_RX_data();
|
||||
debugln("FRSKY_RX_DATA");
|
||||
}
|
||||
}
|
||||
FRSKY_RX_strobe_rx();
|
||||
}
|
||||
return 1000;
|
||||
|
||||
case FRSKY_RX_DATA:
|
||||
if (len == packet_length + 2) //+2=RSSI+LQI+CRC
|
||||
{
|
||||
CC2500_ReadData(packet, len);
|
||||
if(frskyx_rx_check_crc_id(false,false))
|
||||
{
|
||||
RX_RSSI = packet[len-2];
|
||||
if(RX_RSSI >= 128)
|
||||
RX_RSSI -= 128;
|
||||
else
|
||||
RX_RSSI += 128;
|
||||
bool chanskip_valid=true;
|
||||
// hop to next channel
|
||||
if (FRSKY_RX_format != FRSKY_RX_D8)
|
||||
{//D16v1 & D16v2
|
||||
if(rx_data_started)
|
||||
{
|
||||
if(FRSKY_RX_chanskip != (((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2)))
|
||||
{
|
||||
chanskip_valid=false; // chanskip value has changed which surely indicates a bad frame
|
||||
packet_count++;
|
||||
if(packet_count>5) // the TX must have changed chanskip...
|
||||
FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init
|
||||
}
|
||||
else
|
||||
packet_count=0;
|
||||
}
|
||||
else
|
||||
FRSKY_RX_chanskip = ((packet[4] & 0xC0) >> 6) | ((packet[5] & 0x3F) << 2); // chanskip init
|
||||
}
|
||||
hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47;
|
||||
FRSKY_RX_set_channel(hopping_frequency_no);
|
||||
if(chanskip_valid)
|
||||
{
|
||||
if ((telemetry_link & 0x7F) == 0)
|
||||
{ // send channels to TX
|
||||
FRSKY_RX_build_telemetry_packet();
|
||||
telemetry_link = 1;
|
||||
#ifdef SEND_CPPM
|
||||
if(sub_protocol == FRSKY_CPPM)
|
||||
telemetry_link |= 0x80; // Disable telemetry output
|
||||
#endif
|
||||
}
|
||||
pps_counter++;
|
||||
}
|
||||
rx_data_started = true;
|
||||
read_retry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// packets per second
|
||||
if (millis() - pps_timer >= 1000) {
|
||||
pps_timer = millis();
|
||||
debugln("%d pps", pps_counter);
|
||||
RX_LQI = pps_counter;
|
||||
if(pps_counter==0) // no packets for 1 sec or more...
|
||||
{// restart the search
|
||||
rx_data_started=false;
|
||||
packet_count=0;
|
||||
}
|
||||
pps_counter = 0;
|
||||
}
|
||||
|
||||
// skip channel if no packet received in time
|
||||
if (read_retry++ >= 9) {
|
||||
hopping_frequency_no = (hopping_frequency_no + FRSKY_RX_chanskip) % 47;
|
||||
FRSKY_RX_set_channel(hopping_frequency_no);
|
||||
if(rx_data_started)
|
||||
read_retry = 0;
|
||||
else
|
||||
read_retry = -50; // retry longer until first packet is catched
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(FRSKY_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//##########Variables########
|
||||
uint32_t state;
|
||||
uint8_t len;
|
||||
|
||||
enum {
|
||||
FRSKY_BIND = 0,
|
||||
FRSKY_BIND_DONE = 1000,
|
||||
FRSKY_DATA1,
|
||||
FRSKY_DATA2,
|
||||
FRSKY_DATA3,
|
||||
FRSKY_DATA4,
|
||||
FRSKY_DATA5
|
||||
};
|
||||
|
||||
uint16_t initFrSky_2way()
|
||||
{
|
||||
if(IS_AUTOBIND_FLAG_on)
|
||||
{
|
||||
frsky2way_init(1);
|
||||
state = FRSKY_BIND;//
|
||||
}
|
||||
else
|
||||
{
|
||||
frsky2way_init(0);
|
||||
state = FRSKY_DATA2;
|
||||
}
|
||||
return 10000;
|
||||
}
|
||||
|
||||
uint16_t ReadFrSky_2way()
|
||||
{
|
||||
if (state < FRSKY_BIND_DONE)
|
||||
{
|
||||
frsky2way_build_bind_packet();
|
||||
cc2500_strobe(CC2500_SIDLE);
|
||||
cc2500_writeReg(CC2500_0A_CHANNR, 0x00);
|
||||
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
|
||||
cc2500_strobe(CC2500_SFRX);//0x3A
|
||||
cc2500_writeFifo(packet, packet[0]+1);
|
||||
state++;
|
||||
return 9000;
|
||||
}
|
||||
if (state == FRSKY_BIND_DONE)
|
||||
{
|
||||
state = FRSKY_DATA2;
|
||||
frsky2way_init(0);
|
||||
counter = 0;
|
||||
BIND_DONE;
|
||||
}
|
||||
else
|
||||
if (state == FRSKY_DATA5)
|
||||
{
|
||||
cc2500_strobe(CC2500_SRX);//0x34 RX enable
|
||||
state = FRSKY_DATA1;
|
||||
return 9200;
|
||||
}
|
||||
counter = (counter + 1) % 188;
|
||||
if (state == FRSKY_DATA4)
|
||||
{ //telemetry receive
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
cc2500_strobe(CC2500_SIDLE);
|
||||
cc2500_writeReg(CC2500_0A_CHANNR, get_chan_num(counter % 47));
|
||||
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
|
||||
state++;
|
||||
return 1300;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state == FRSKY_DATA1)
|
||||
{
|
||||
len = cc2500_readReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if (len)//20 bytes
|
||||
{
|
||||
cc2500_readFifo(pkt, len); //received telemetry packets
|
||||
#if defined(TELEMETRY)
|
||||
//parse telemetry packet here
|
||||
check_telemetry(pkt,len); //check if valid telemetry packets and buffer them.
|
||||
#endif
|
||||
}
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower(); // Set tx_power
|
||||
}
|
||||
cc2500_strobe(CC2500_SIDLE);
|
||||
cc2500_writeReg(CC2500_0A_CHANNR, get_chan_num(counter % 47));
|
||||
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
|
||||
cc2500_strobe(CC2500_SFRX);
|
||||
frsky2way_data_frame();
|
||||
cc2500_writeFifo(packet, packet[0]+1);
|
||||
state++;
|
||||
}
|
||||
return state == FRSKY_DATA4 ? 7500 : 9000;
|
||||
}
|
||||
|
||||
#if defined(TELEMETRY)
|
||||
void check_telemetry(uint8_t *pkt,uint8_t len)
|
||||
{
|
||||
if(pkt[1] != rx_tx_addr[3] || pkt[2] != rx_tx_addr[2] || len != pkt[0] + 3)
|
||||
{//only packets with the required id and packet length
|
||||
for(uint8_t i=3;i<6;i++)
|
||||
pktt[i]=0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t i=3;i<len;i++)
|
||||
pktt[i]=pkt[i];
|
||||
telemetry_link=1;
|
||||
}
|
||||
}
|
||||
|
||||
void compute_RSSIdbm(){
|
||||
if(pktt[len-2] >=128){
|
||||
RSSI_dBm =(((uint16_t)(pktt[len-2])*18)>>5)- 82;
|
||||
}
|
||||
else{
|
||||
RSSI_dBm = (((uint16_t)(pktt[len-2])*18)>>5)+65;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void frsky2way_init(uint8_t bind)
|
||||
{
|
||||
// Configure cc2500 for tx mode
|
||||
CC2500_Reset();
|
||||
//
|
||||
cc2500_writeReg(CC2500_02_IOCFG0, 0x06);
|
||||
cc2500_writeReg(CC2500_00_IOCFG2, 0x06);
|
||||
cc2500_writeReg(CC2500_17_MCSM1, 0x0c);
|
||||
cc2500_writeReg(CC2500_18_MCSM0, 0x18);
|
||||
cc2500_writeReg(CC2500_06_PKTLEN, 0x19);
|
||||
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x04);
|
||||
cc2500_writeReg(CC2500_08_PKTCTRL0, 0x05);
|
||||
cc2500_writeReg(CC2500_3E_PATABLE, 0xff);
|
||||
cc2500_writeReg(CC2500_0B_FSCTRL1, 0x08);
|
||||
cc2500_writeReg(CC2500_0C_FSCTRL0, fine);
|
||||
//base freq FREQ = 0x5C7627 (F = 2404MHz)
|
||||
cc2500_writeReg(CC2500_0D_FREQ2, 0x5c);
|
||||
cc2500_writeReg(CC2500_0E_FREQ1, 0x76);
|
||||
cc2500_writeReg(CC2500_0F_FREQ0, 0x27);
|
||||
//
|
||||
cc2500_writeReg(CC2500_10_MDMCFG4, 0xAA);
|
||||
cc2500_writeReg(CC2500_11_MDMCFG3, 0x39);
|
||||
cc2500_writeReg(CC2500_12_MDMCFG2, 0x11);
|
||||
cc2500_writeReg(CC2500_13_MDMCFG1, 0x23);
|
||||
cc2500_writeReg(CC2500_14_MDMCFG0, 0x7a);
|
||||
cc2500_writeReg(CC2500_15_DEVIATN, 0x42);
|
||||
cc2500_writeReg(CC2500_19_FOCCFG, 0x16);
|
||||
cc2500_writeReg(CC2500_1A_BSCFG, 0x6c);
|
||||
cc2500_writeReg(CC2500_1B_AGCCTRL2, bind ? 0x43 : 0x03);
|
||||
cc2500_writeReg(CC2500_1C_AGCCTRL1,0x40);
|
||||
cc2500_writeReg(CC2500_1D_AGCCTRL0,0x91);
|
||||
cc2500_writeReg(CC2500_21_FREND1, 0x56);
|
||||
cc2500_writeReg(CC2500_22_FREND0, 0x10);
|
||||
cc2500_writeReg(CC2500_23_FSCAL3, 0xa9);
|
||||
cc2500_writeReg(CC2500_24_FSCAL2, 0x0A);
|
||||
cc2500_writeReg(CC2500_25_FSCAL1, 0x00);
|
||||
cc2500_writeReg(CC2500_26_FSCAL0, 0x11);
|
||||
cc2500_writeReg(CC2500_29_FSTEST, 0x59);
|
||||
cc2500_writeReg(CC2500_2C_TEST2, 0x88);
|
||||
cc2500_writeReg(CC2500_2D_TEST1, 0x31);
|
||||
cc2500_writeReg(CC2500_2E_TEST0, 0x0B);
|
||||
cc2500_writeReg(CC2500_03_FIFOTHR, 0x07);
|
||||
cc2500_writeReg(CC2500_09_ADDR, 0x00);
|
||||
//
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
|
||||
cc2500_strobe(CC2500_SIDLE);
|
||||
|
||||
cc2500_writeReg(CC2500_09_ADDR, bind ? 0x03 : rx_tx_addr[3]);
|
||||
cc2500_writeReg(CC2500_07_PKTCTRL1, 0x05);
|
||||
cc2500_strobe(CC2500_SIDLE); // Go to idle...
|
||||
//
|
||||
cc2500_writeReg(CC2500_0A_CHANNR, 0x00);
|
||||
cc2500_writeReg(CC2500_23_FSCAL3, 0x89);
|
||||
cc2500_strobe(CC2500_SFRX);
|
||||
//#######END INIT########
|
||||
}
|
||||
|
||||
uint8_t get_chan_num(uint16_t idx)
|
||||
{
|
||||
uint8_t ret = (idx * 0x1e) % 0xeb;
|
||||
if(idx == 3 || idx == 23 || idx == 47)
|
||||
ret++;
|
||||
if(idx > 47)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void frsky2way_build_bind_packet()
|
||||
{
|
||||
//11 03 01 d7 2d 00 00 1e 3c 5b 78 00 00 00 00 00 00 01
|
||||
//11 03 01 19 3e 00 02 8e 2f bb 5c 00 00 00 00 00 00 01
|
||||
packet[0] = 0x11;
|
||||
packet[1] = 0x03;
|
||||
packet[2] = 0x01;
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[4] = rx_tx_addr[2];
|
||||
uint16_t idx = ((state -FRSKY_BIND) % 10) * 5;
|
||||
packet[5] = idx;
|
||||
packet[6] = get_chan_num(idx++);
|
||||
packet[7] = get_chan_num(idx++);
|
||||
packet[8] = get_chan_num(idx++);
|
||||
packet[9] = get_chan_num(idx++);
|
||||
packet[10] = get_chan_num(idx++);
|
||||
packet[11] = 0x00;
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x00;
|
||||
packet[16] = 0x00;
|
||||
packet[17] = 0x01;
|
||||
}
|
||||
|
||||
uint8_t telemetry_counter=0;
|
||||
|
||||
void frsky2way_data_frame()
|
||||
{//pachet[4] is telemetry user frame counter(hub)
|
||||
//11 d7 2d 22 00 01 c9 c9 ca ca 88 88 ca ca c9 ca 88 88
|
||||
//11 57 12 00 00 01 f2 f2 f2 f2 06 06 ca ca ca ca 18 18
|
||||
packet[0] = 0x11; //Length
|
||||
packet[1] = rx_tx_addr[3];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = counter;//
|
||||
packet[4] = pkt[6]?(telemetry_counter++)%32:0;
|
||||
packet[5] = 0x01;
|
||||
//
|
||||
packet[10] = 0;
|
||||
packet[11] = 0;
|
||||
packet[16] = 0;
|
||||
packet[17] = 0;
|
||||
for(uint8_t i = 0; i < 8; i++)
|
||||
{
|
||||
uint16_t value;
|
||||
value = convert_channel_frsky(i);
|
||||
if(i < 4)
|
||||
{
|
||||
packet[6+i] = value & 0xff;
|
||||
packet[10+(i>>1)] |= ((value >> 8) & 0x0f) << (4 *(i & 0x01));
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[8+i] = value & 0xff;
|
||||
packet[16+((i-4)>>1)] |= ((value >> 8) & 0x0f) << (4 * ((i-4) & 0x01));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
314
Multiprotocol/Futaba_cc2500.ino
Normal file
314
Multiprotocol/Futaba_cc2500.ino
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with main deviation/sfhss_cc2500.c dated 2016-03-23
|
||||
|
||||
#if defined(FUTABA_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define SFHSS_DEBUG_TIMING
|
||||
|
||||
#define SFHSS_COARSE 0
|
||||
|
||||
#define SFHSS_PACKET_LEN 13
|
||||
#define SFHSS_TX_ID_LEN 2
|
||||
|
||||
uint8_t fhss_code=0; // 0-27
|
||||
|
||||
enum {
|
||||
SFHSS_START = 0x00,
|
||||
SFHSS_CAL = 0x01,
|
||||
SFHSS_DATA1 = 0x02,
|
||||
SFHSS_DATA2 = 0x03,
|
||||
SFHSS_TUNE = 0x04
|
||||
};
|
||||
|
||||
#define SFHSS_FREQ0_VAL 0xC4
|
||||
|
||||
// Some important initialization parameters, all others are either default,
|
||||
// or not important in the context of transmitter
|
||||
// IOCFG2 2F - GDO2_INV=0 GDO2_CFG=2F - HW0
|
||||
// IOCFG1 2E - GDO1_INV=0 GDO1_CFG=2E - High Impedance
|
||||
// IOCFG0 2F - GDO0 same as GDO2, TEMP_SENSOR_ENABLE=off
|
||||
// FIFOTHR 07 - 33 decimal TX threshold
|
||||
// SYNC1 D3
|
||||
// SYNC0 91
|
||||
// PKTLEN 0D - Packet length, 0D bytes
|
||||
// PKTCTRL1 04 - APPEND_STATUS on, all other are receive parameters - irrelevant
|
||||
// PKTCTRL0 0C - No whitening, use FIFO, CC2400 compatibility on, use CRC, fixed packet length
|
||||
// ADDR 29
|
||||
// CHANNR 10
|
||||
// FSCTRL1 06 - IF 152343.75Hz, see page 65
|
||||
// FSCTRL0 00 - zero freq offset
|
||||
// FREQ2 5C - synthesizer frequency 2399999633Hz for 26MHz crystal, ibid
|
||||
// FREQ1 4E
|
||||
// FREQ0 C4
|
||||
// MDMCFG4 7C - CHANBW_E - 01, CHANBW_M - 03, DRATE_E - 0C. Filter bandwidth = 232142Hz
|
||||
// MDMCFG3 43 - DRATE_M - 43. Data rate = 128143bps
|
||||
// MDMCFG2 83 - disable DC blocking, 2-FSK, no Manchester code, 15/16 sync bits detected (irrelevant for TX)
|
||||
// MDMCFG1 23 - no FEC, 4 preamble bytes, CHANSPC_E - 03
|
||||
// MDMCFG0 3B - CHANSPC_M - 3B. Channel spacing = 249938Hz (each 6th channel used, resulting in spacing of 1499628Hz)
|
||||
// DEVIATN 44 - DEVIATION_E - 04, DEVIATION_M - 04. Deviation = 38085.9Hz
|
||||
// MCSM2 07 - receive parameters, default, irrelevant
|
||||
// MCSM1 0C - no CCA (transmit always), when packet received stay in RX, when sent go to IDLE
|
||||
// MCSM0 08 - no autocalibration, PO_TIMEOUT - 64, no pin radio control, no forcing XTAL to stay in SLEEP
|
||||
// FOCCFG 1D - not interesting, Frequency Offset Compensation
|
||||
// FREND0 10 - PA_POWER = 0
|
||||
const PROGMEM uint8_t SFHSS_init_values[] = {
|
||||
/* 00 */ 0x2F, 0x2E, 0x2F, 0x07, 0xD3, 0x91, 0x0D, 0x04,
|
||||
/* 08 */ 0x0C, 0x29, 0x10, 0x06, 0x00, 0x5C, 0x4E, SFHSS_FREQ0_VAL + SFHSS_COARSE,
|
||||
/* 10 */ 0x7C, 0x43, 0x83, 0x23, 0x3B, 0x44, 0x07, 0x0C,
|
||||
/* 18 */ 0x08, 0x1D, 0x1C, 0x43, 0x40, 0x91, 0x57, 0x6B,
|
||||
/* 20 */ 0xF8, 0xB6, 0x10, 0xEA, 0x0A, 0x11, 0x11
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) SFHSS_rf_init()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
for (uint8_t i = 0; i < 39; ++i)
|
||||
CC2500_WriteReg(i, pgm_read_byte_near(&SFHSS_init_values[i]));
|
||||
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) SFHSS_tune_chan()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, rf_ch_num*6+16);
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) SFHSS_tune_chan_fast()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, rf_ch_num*6+16);
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[rf_ch_num]);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) SFHSS_tune_freq()
|
||||
{
|
||||
if ( prev_option != option )
|
||||
{
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
CC2500_WriteReg(CC2500_0F_FREQ0, SFHSS_FREQ0_VAL + SFHSS_COARSE);
|
||||
prev_option = option ;
|
||||
phase = SFHSS_START; // Restart the tune process if option is changed to get good tuned values
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) SFHSS_calc_next_chan()
|
||||
{
|
||||
rf_ch_num += fhss_code + 2;
|
||||
if (rf_ch_num > 29)
|
||||
{
|
||||
if (rf_ch_num < 31)
|
||||
rf_ch_num += fhss_code + 2;
|
||||
rf_ch_num -= 31;
|
||||
}
|
||||
}
|
||||
|
||||
// Channel values are 12-bit values between 1020 and 2020, 1520 is the middle.
|
||||
// Futaba @140% is 2070...1520...970
|
||||
// Values grow down and to the right.
|
||||
static void __attribute__((unused)) SFHSS_send_packet()
|
||||
{
|
||||
uint16_t ch[4];
|
||||
// command.bit0 is the packet number indicator: =0 -> SFHSS_DATA1, =1 -> SFHSS_DATA2
|
||||
// command.bit1 is unknown but seems to be linked to the payload[0].bit0 but more dumps are needed: payload[0]=0x82 -> =0, payload[0]=0x81 -> =1
|
||||
// command.bit2 is the failsafe transmission indicator: =0 -> normal data, =1->failsafe data
|
||||
// command.bit3 is the channels indicator: =0 -> CH1-4, =1 -> CH5-8
|
||||
|
||||
//Coding below matches the Futaba T8J transmission scheme DATA1->CH1-4, DATA2->CH5-8, DATA1->CH5-8, DATA2->CH1-4,...
|
||||
// XK, T10J and TM-FH are different with a classic DATA1->CH1-4, DATA2->CH5-8,...
|
||||
//Failsafe is sent twice every couple of seconds (unknown but >5s)
|
||||
|
||||
uint8_t command= (phase == SFHSS_DATA1) ? 0 : 1; // Building packet for Data1 or Data2
|
||||
counter+=command;
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if( (counter&0x3FC) == 0x3FC && IS_FAILSAFE_VALUES_on)
|
||||
{ // Transmit failsafe data twice every 7s
|
||||
if( ((counter&1)^(command&1)) == 0 )
|
||||
command|=0x04; // Failsafe
|
||||
}
|
||||
else
|
||||
#endif
|
||||
command|=0x02; // Assuming packet[0] == 0x81
|
||||
counter&=0x3FF; // Reset failsafe counter
|
||||
if(counter&1) command|=0x08; // Transmit lower and upper channels twice in a row
|
||||
|
||||
uint8_t ch_offset = (command&0x08) >> 1; // CH1..CH4 or CH5..CH8
|
||||
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(command&0x04)
|
||||
{ //Failsafe data are:
|
||||
// 0 to 1023 -> no output on channel
|
||||
// 1024-2047 -> hold output on channel
|
||||
// 2048-4095 -> channel_output=(data&0x3FF)*5/4+880 in µs
|
||||
// Notes:
|
||||
// 2048-2559 -> does not look valid since it only covers the range from 1520µs to 2160µs
|
||||
// 2560-3583 -> valid for any channel values from 880µs to 2160µs
|
||||
// 3584-4095 -> looks to be used for the throttle channel with values ranging from 880µs to 1520µs
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
{
|
||||
uint16_t val=Failsafe_data[CH_AETR[ch_offset+i]];
|
||||
if(val==FAILSAFE_CHANNEL_HOLD)
|
||||
ch[i]=1024;
|
||||
else if(val==FAILSAFE_CHANNEL_NOPULSES)
|
||||
ch[i]=0;
|
||||
else
|
||||
{ //Use channel value
|
||||
ch[i] = convert_channel_16b_nolimit(CH_AETR[ch_offset+i],3571,2571,true); //3472,2672: not enough throw
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{ //Normal data
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
ch[i] = convert_channel_16b_nolimit(CH_AETR[ch_offset+i],2020,1020,false);
|
||||
}
|
||||
|
||||
|
||||
// XK [0]=0x81 [3]=0x00 [4]=0x00
|
||||
// T8J [0]=0x81 [3]=0x42 [4]=0x07
|
||||
// T10J [0]=0x81 [3]=0x0F [4]=0x09
|
||||
// TM-FH [0]=0x82 [3]=0x9A [4]=0x06
|
||||
packet[0] = 0x81; // can be 80 or 81 for Orange, only 81 for XK
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
packet[3] = 0x00; // unknown but prevents some receivers to bind if not 0
|
||||
packet[4] = 0x00; // unknown but prevents some receivers to bind if not 0
|
||||
packet[5] = (rf_ch_num << 3) | ((ch[0] >> 9) & 0x07);
|
||||
packet[6] = (ch[0] >> 1);
|
||||
packet[7] = (ch[0] << 7) | ((ch[1] >> 5) & 0x7F );
|
||||
packet[8] = (ch[1] << 3) | ((ch[2] >> 9) & 0x07 );
|
||||
packet[9] = (ch[2] >> 1);
|
||||
packet[10] = (ch[2] << 7) | ((ch[3] >> 5) & 0x7F );
|
||||
packet[11] = (ch[3] << 3) | ((fhss_code >> 2) & 0x07 );
|
||||
packet[12] = (fhss_code << 6) | command;
|
||||
|
||||
CC2500_WriteData(packet, SFHSS_PACKET_LEN);
|
||||
}
|
||||
|
||||
uint16_t SFHSS_callback()
|
||||
{
|
||||
#ifdef SFHSS_DEBUG_TIMING
|
||||
static uint16_t prev_adjust_timing=1024;
|
||||
uint16_t adjust_timing = (Channel_data[CH15]>>3) - (1024>>3); // +-102 @ 100%
|
||||
#endif
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case SFHSS_START:
|
||||
rf_ch_num = 0;
|
||||
SFHSS_tune_chan();
|
||||
phase = SFHSS_CAL;
|
||||
return 2000;
|
||||
case SFHSS_CAL:
|
||||
calData[rf_ch_num]=CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
if (++rf_ch_num < 30)
|
||||
SFHSS_tune_chan();
|
||||
else
|
||||
{
|
||||
rf_ch_num = 0;
|
||||
counter = 0;
|
||||
phase = SFHSS_DATA1;
|
||||
}
|
||||
return 2000;
|
||||
|
||||
/* Work cycle: 6.8ms */
|
||||
#define SFHSS_PACKET_PERIOD 6800
|
||||
#define SFHSS_DATA2_TIMING 1625 // Adjust this value between 1600 and 1650 if your RX(s) are not operating properly
|
||||
case SFHSS_DATA1:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(6800);
|
||||
#endif
|
||||
SFHSS_send_packet();
|
||||
phase = SFHSS_DATA2;
|
||||
#ifdef SFHSS_DEBUG_TIMING
|
||||
return SFHSS_DATA2_TIMING - adjust_timing;
|
||||
#else
|
||||
return SFHSS_DATA2_TIMING; // original 1650
|
||||
#endif
|
||||
case SFHSS_DATA2:
|
||||
SFHSS_send_packet();
|
||||
SFHSS_calc_next_chan();
|
||||
phase = SFHSS_TUNE;
|
||||
#ifdef SFHSS_DEBUG_TIMING
|
||||
if(prev_adjust_timing != adjust_timing)
|
||||
{
|
||||
debugln("A:%d",(uint16_t)(SFHSS_DATA2_TIMING - adjust_timing));
|
||||
prev_adjust_timing = adjust_timing;
|
||||
}
|
||||
return SFHSS_PACKET_PERIOD -2000 -(SFHSS_DATA2_TIMING - adjust_timing);
|
||||
#else
|
||||
return SFHSS_PACKET_PERIOD -2000 -SFHSS_DATA2_TIMING; // original 2000
|
||||
#endif
|
||||
case SFHSS_TUNE:
|
||||
phase = SFHSS_DATA1;
|
||||
SFHSS_tune_freq();
|
||||
SFHSS_tune_chan_fast();
|
||||
CC2500_SetPower();
|
||||
return 2000; // original 3150
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate internal id
|
||||
static void __attribute__((unused)) SFHSS_get_tx_id()
|
||||
{
|
||||
// Some receivers (Orange) behaves better if they tuned to id that has
|
||||
// no more than 6 consecutive zeros and ones
|
||||
uint32_t fixed_id;
|
||||
uint8_t run_count = 0;
|
||||
// add guard for bit count
|
||||
fixed_id = 1 ^ (MProtocol_id & 1);
|
||||
for (uint8_t i = 0; i < 16; ++i)
|
||||
{
|
||||
fixed_id = (fixed_id << 1) | (MProtocol_id & 1);
|
||||
MProtocol_id >>= 1;
|
||||
// If two LS bits are the same
|
||||
if ((fixed_id & 3) == 0 || (fixed_id & 3) == 3)
|
||||
{
|
||||
if (++run_count > 6)
|
||||
{
|
||||
fixed_id ^= 1;
|
||||
run_count = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
run_count = 0;
|
||||
}
|
||||
// fixed_id = 0xBC11;
|
||||
rx_tx_addr[0] = fixed_id >> 8;
|
||||
rx_tx_addr[1] = fixed_id >> 0;
|
||||
}
|
||||
|
||||
void SFHSS_init()
|
||||
{
|
||||
BIND_DONE; // Not a TX bind protocol
|
||||
SFHSS_get_tx_id();
|
||||
|
||||
fhss_code=random(0xfefefefe)%28; // Initialize it to random 0-27 inclusive
|
||||
|
||||
SFHSS_rf_init();
|
||||
phase = SFHSS_START;
|
||||
}
|
||||
|
||||
#endif
|
||||
230
Multiprotocol/GD00X_ccnrf.ino
Normal file
230
Multiprotocol/GD00X_ccnrf.ino
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with GD005 C-17 and GD006 DA62 planes.
|
||||
|
||||
#if defined(GD00X_CCNRF_INO)
|
||||
|
||||
#include "iface_nrf250k.h"
|
||||
|
||||
//#define FORCE_GD00X_ORIGINAL_ID
|
||||
|
||||
#define GD00X_INITIAL_WAIT 500
|
||||
#define GD00X_PACKET_PERIOD 3500
|
||||
#define GD00X_RF_BIND_CHANNEL 2
|
||||
#define GD00X_RF_NUM_CHANNELS 4
|
||||
#define GD00X_PAYLOAD_SIZE 15
|
||||
#define GD00X_BIND_COUNT 857 //3sec
|
||||
|
||||
#define GD00X_V2_BIND_PACKET_PERIOD 5110
|
||||
#define GD00X_V2_RF_BIND_CHANNEL 0x43
|
||||
#define GD00X_V2_RF_NUM_CHANNELS 2
|
||||
#define GD00X_V2_PAYLOAD_SIZE 6
|
||||
|
||||
// flags going to packet[11]
|
||||
#define GD00X_FLAG_DR 0x08
|
||||
#define GD00X_FLAG_LIGHT 0x04
|
||||
|
||||
// flags going to packet[4]
|
||||
#define GD00X_V2_FLAG_DR 0x40
|
||||
#define GD00X_V2_FLAG_LIGHT 0x80
|
||||
|
||||
static void __attribute__((unused)) GD00X_send_packet()
|
||||
{
|
||||
static uint8_t prev_CH6=false;
|
||||
|
||||
if(sub_protocol==GD_V1)
|
||||
{
|
||||
packet[0] = IS_BIND_IN_PROGRESS?0xAA:0x55;
|
||||
memcpy(packet+1,rx_tx_addr,4);
|
||||
uint16_t channel=convert_channel_ppm(AILERON);
|
||||
packet[5 ] = channel;
|
||||
packet[6 ] = channel>>8;
|
||||
channel=convert_channel_ppm(THROTTLE);
|
||||
packet[7 ] = channel;
|
||||
packet[8 ] = channel>>8;
|
||||
channel=convert_channel_ppm(CH5); // TRIM
|
||||
packet[9 ] = channel;
|
||||
packet[10] = channel>>8;
|
||||
packet[11] = GET_FLAG(!CH7_SW, GD00X_FLAG_DR)
|
||||
| GET_FLAG(CH6_SW, GD00X_FLAG_LIGHT);
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = 0x00;
|
||||
}
|
||||
else
|
||||
{//GD_V2
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
for(uint8_t i=0; i<5;i++)
|
||||
packet[i]=rx_tx_addr[i];
|
||||
else
|
||||
{
|
||||
packet[0]=convert_channel_16b_limit(THROTTLE,0,100); // 0..100
|
||||
|
||||
// Deadband is needed on aileron, 40 gives +-6%
|
||||
packet[1]=convert_channel_8b_limit_deadband(AILERON,0x3F,0x20,0x00,40); // Aileron: 3F..20..00
|
||||
// Trims must be in a seperate channel for this model
|
||||
packet[2]=0x3F-(convert_channel_8b(CH5)>>2); // Trim: 0x3F..0x20..0x00
|
||||
|
||||
uint8_t seq=((packet_count*3)/7)%5;
|
||||
packet[4]=seq
|
||||
| GET_FLAG(!CH7_SW, GD00X_V2_FLAG_DR);
|
||||
|
||||
if(CH6_SW!=prev_CH6)
|
||||
{ // LED switch is temporary
|
||||
len=43;
|
||||
prev_CH6=CH6_SW;
|
||||
}
|
||||
if(len)
|
||||
{ // Send the light flag for a couple of packets
|
||||
packet[4] |= GD00X_V2_FLAG_LIGHT;
|
||||
len--;
|
||||
}
|
||||
|
||||
packet[3]=(packet[0]+packet[1]+packet[2]+packet[4])^(crc8);
|
||||
|
||||
if( (packet_count%12) == 0 )
|
||||
hopping_frequency_no ^= 1; // Toggle between the 2 frequencies
|
||||
packet_count++;
|
||||
if(packet_count>34) packet_count=0; // Full period
|
||||
if( seq == (((packet_count*3)/7)%5) )
|
||||
{
|
||||
if(packet_period==2700)
|
||||
packet_period=3000;
|
||||
else
|
||||
packet_period=2700;
|
||||
}
|
||||
else
|
||||
packet_period=4300;
|
||||
}
|
||||
packet[5]='D';
|
||||
}
|
||||
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
XN297L_Hopping(hopping_frequency_no);
|
||||
if(sub_protocol==GD_V1)
|
||||
{
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no &= GD00X_RF_NUM_CHANNELS-1; // 4 RF channels
|
||||
}
|
||||
}
|
||||
|
||||
XN297L_WritePayload(packet, packet_length);
|
||||
|
||||
XN297L_SetPower(); // Set tx_power
|
||||
XN297L_SetFreqOffset(); // Set frequency offset
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) GD00X_RF_init()
|
||||
{
|
||||
XN297L_Init();
|
||||
if(sub_protocol==GD_V1)
|
||||
XN297L_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
else
|
||||
XN297L_SetTXAddr((uint8_t*)"GDKNx", 5);
|
||||
XN297L_HoppingCalib(sub_protocol==GD_V1?GD00X_RF_NUM_CHANNELS:GD00X_V2_RF_NUM_CHANNELS); // Calibrate all channels
|
||||
XN297L_RFChannel(sub_protocol==GD_V1?GD00X_RF_BIND_CHANNEL:GD00X_V2_RF_BIND_CHANNEL); // Set bind channel
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) GD00X_initialize_txid()
|
||||
{
|
||||
if(sub_protocol==GD_V1)
|
||||
{
|
||||
uint8_t start=76+(rx_tx_addr[0]&0x03);
|
||||
for(uint8_t i=0; i<GD00X_RF_NUM_CHANNELS;i++)
|
||||
hopping_frequency[i]=start-(i<<1);
|
||||
#ifdef FORCE_GD00X_ORIGINAL_ID
|
||||
rx_tx_addr[0]=0x1F; // or 0xA5 or 0x26
|
||||
rx_tx_addr[1]=0x39; // or 0x37 or 0x35
|
||||
rx_tx_addr[2]=0x12; // Constant on 3 TXs
|
||||
rx_tx_addr[3]=0x13; // Constant on 3 TXs
|
||||
for(uint8_t i=0; i<GD00X_RF_NUM_CHANNELS;i++)
|
||||
hopping_frequency[i]=79-(i<<1); // or 77 or 78
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//Generate 64 different IDs
|
||||
rx_tx_addr[1]=0x00;
|
||||
rx_tx_addr[2]=0x00;
|
||||
rx_tx_addr[1+((rx_tx_addr[3]&0x10)>>4)]=rx_tx_addr[3]&0x8F;
|
||||
rx_tx_addr[0]=0x65;
|
||||
rx_tx_addr[3]=0x95;
|
||||
rx_tx_addr[4]=0x47; //'G'
|
||||
|
||||
crc8=rx_tx_addr[0]^rx_tx_addr[1]^rx_tx_addr[2];
|
||||
//hopping calculation
|
||||
hopping_frequency[0]=(0x15+(crc8^rx_tx_addr[3]))&0x1F;
|
||||
if( hopping_frequency[0] == 0x0F )
|
||||
hopping_frequency[0]=0x0E;
|
||||
else if( (hopping_frequency[0]&0xFE) == 0x10 )
|
||||
hopping_frequency[0]+=2;
|
||||
hopping_frequency[1]=0x20+hopping_frequency[0];
|
||||
|
||||
#ifdef FORCE_GD00X_ORIGINAL_ID
|
||||
//ID 1
|
||||
rx_tx_addr[0]=0x65;
|
||||
rx_tx_addr[1]=0x00;
|
||||
rx_tx_addr[2]=0x00;
|
||||
rx_tx_addr[3]=0x95;
|
||||
rx_tx_addr[4]=0x47; //'G'
|
||||
hopping_frequency[0]=0x05;
|
||||
hopping_frequency[1]=0x25;
|
||||
//ID 2
|
||||
rx_tx_addr[0]=0xFD;
|
||||
rx_tx_addr[1]=0x09;
|
||||
rx_tx_addr[2]=0x00;
|
||||
rx_tx_addr[3]=0x65;
|
||||
rx_tx_addr[4]=0x47; //'G'
|
||||
hopping_frequency[0]=0x06;
|
||||
hopping_frequency[1]=0x26;
|
||||
//ID 3
|
||||
rx_tx_addr[0]=0x67;
|
||||
rx_tx_addr[1]=0x0F;
|
||||
rx_tx_addr[2]=0x00;
|
||||
rx_tx_addr[3]=0x69;
|
||||
rx_tx_addr[4]=0x47; //'G'
|
||||
hopping_frequency[0]=0x16;
|
||||
hopping_frequency[1]=0x36;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t GD00X_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
BIND_DONE;
|
||||
GD00X_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void GD00X_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
GD00X_initialize_txid();
|
||||
GD00X_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
bind_counter=GD00X_BIND_COUNT;
|
||||
packet_period=sub_protocol==GD_V1?GD00X_PACKET_PERIOD:GD00X_V2_BIND_PACKET_PERIOD;
|
||||
packet_length=sub_protocol==GD_V1?GD00X_PAYLOAD_SIZE:GD00X_V2_PAYLOAD_SIZE;
|
||||
packet_count=0;
|
||||
len=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
147
Multiprotocol/GW008_nrf24l01.ino
Normal file
147
Multiprotocol/GW008_nrf24l01.ino
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with Global Drone GW008 protocol.
|
||||
// There are 3 versions of this small quad, this protocol is for the one with a XNS104 IC in the stock Tx and PAN159CY IC in the quad (SOCs with built-in xn297 compatible RF).
|
||||
// The xn297 version is compatible with the CX10 protocol (green pcb).
|
||||
// The LT8910 version is not supported yet.
|
||||
|
||||
#if defined(GW008_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define GW008_INITIAL_WAIT 500
|
||||
#define GW008_PACKET_PERIOD 2400
|
||||
#define GW008_RF_BIND_CHANNEL 2
|
||||
#define GW008_PAYLOAD_SIZE 15
|
||||
|
||||
enum {
|
||||
GW008_BIND1,
|
||||
GW008_BIND2,
|
||||
GW008_DATA
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) GW008_send_packet()
|
||||
{
|
||||
packet[0] = rx_tx_addr[0];
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[1] = 0x55;
|
||||
packet[2] = hopping_frequency[0];
|
||||
packet[3] = hopping_frequency[1];
|
||||
packet[4] = hopping_frequency[2];
|
||||
packet[5] = hopping_frequency[3];
|
||||
memset(&packet[6], 0, 7);
|
||||
packet[13] = 0xaa;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[1] = 0x01 | GET_FLAG(CH5_SW, 0x40); // flip
|
||||
packet[2] = convert_channel_16b_limit(AILERON , 200, 0); // aileron
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR, 0, 200); // elevator
|
||||
packet[4] = convert_channel_16b_limit(RUDDER , 200, 0); // rudder
|
||||
packet[5] = convert_channel_16b_limit(THROTTLE, 0, 200); // throttle
|
||||
packet[6] = 0xaa;
|
||||
packet[7] = 0x02; // max rate
|
||||
packet[8] = 0x00;
|
||||
packet[9] = 0x00;
|
||||
packet[10]= 0x00;
|
||||
packet[11]= 0x00;
|
||||
packet[12]= 0x00;
|
||||
packet[13]= rx_tx_addr[2];
|
||||
}
|
||||
packet[14] = rx_tx_addr[1];
|
||||
|
||||
// Power on, TX mode, CRC enabled
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? GW008_RF_BIND_CHANNEL : hopping_frequency[(hopping_frequency_no++)/2]);
|
||||
hopping_frequency_no %= 8;
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WriteEnhancedPayload(packet, GW008_PAYLOAD_SIZE, 0);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) GW008_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
XN297_SetRXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, GW008_PAYLOAD_SIZE+2); // payload + 2 bytes for pcf
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) GW008_initialize_txid()
|
||||
{
|
||||
uint32_t lfsr = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
hopping_frequency[i] = 0x10 + ((lfsr >> (i*8)) % 0x37);
|
||||
}
|
||||
|
||||
uint16_t GW008_callback()
|
||||
{
|
||||
switch(phase)
|
||||
{
|
||||
case GW008_BIND1:
|
||||
if((NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) && // RX fifo data ready
|
||||
XN297_ReadEnhancedPayload(packet, GW008_PAYLOAD_SIZE) == GW008_PAYLOAD_SIZE && // check payload size
|
||||
packet[0] == rx_tx_addr[0] && packet[14] == rx_tx_addr[1]) // check tx id
|
||||
{
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
rx_tx_addr[2] = packet[13];
|
||||
BIND_DONE;
|
||||
phase = GW008_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
GW008_send_packet();
|
||||
phase = GW008_BIND2;
|
||||
return 850; // minimum value 750 for STM32
|
||||
}
|
||||
break;
|
||||
case GW008_BIND2:
|
||||
// switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
|
||||
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
|
||||
phase = GW008_BIND1;
|
||||
return 5000;
|
||||
break;
|
||||
case GW008_DATA:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(GW008_PACKET_PERIOD);
|
||||
#endif
|
||||
GW008_send_packet();
|
||||
break;
|
||||
}
|
||||
return GW008_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void GW008_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
GW008_initialize_txid();
|
||||
phase = GW008_BIND1;
|
||||
GW008_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
248
Multiprotocol/H8_3D_nrf24l01.ino
Normal file
248
Multiprotocol/H8_3D_nrf24l01.ino
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with EAchine 3D X4, CG023/CG031, Attop YD-822/YD-829/YD-829C and H8_3D/JJRC H20/H22
|
||||
// Merged CG023 and H8_3D protocols
|
||||
// Last sync with hexfet new_protocols/cg023_nrf24l01.c dated 2015-10-03
|
||||
// Last sync with hexfet new_protocols/h8_3d_nrf24l01.c dated 2015-11-18
|
||||
|
||||
#if defined(H8_3D_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define H8_3D_PACKET_PERIOD 1800
|
||||
#define H20H_PACKET_PERIOD 9340
|
||||
#define H20MINI_PACKET_PERIOD 3100
|
||||
#define H8_3D_INITIAL_WAIT 500
|
||||
#define H8_3D_PACKET_SIZE 20
|
||||
#define H8_3D_RF_NUM_CHANNELS 4
|
||||
#define H20H_BIND_RF 0x49
|
||||
#define H8_3D_BIND_COUNT 1000
|
||||
|
||||
enum H8_3D_FLAGS {
|
||||
// flags going to packet[17]
|
||||
H8_3D_FLAG_FLIP = 0x01,
|
||||
H8_3D_FLAG_RATE_MID = 0x02,
|
||||
H8_3D_FLAG_RATE_HIGH = 0x04,
|
||||
H8_3D_FLAG_LIGTH = 0x08, // Light on H22
|
||||
H8_3D_FLAG_HEADLESS = 0x10, // RTH + headless on H8, headless on JJRC H20, RTH on H22
|
||||
H8_3D_FLAG_RTH = 0x20, // 360 flip mode on H8 3D and H22, RTH on JJRC H20
|
||||
};
|
||||
|
||||
enum H8_3D_FLAGS_2 {
|
||||
// flags going to packet[18]
|
||||
H8_3D_FLAG_VIDEO = 0x80,
|
||||
H8_3D_FLAG_PICTURE = 0x40,
|
||||
H8_3D_FLAG_CALIBRATE1 = 0x20, // H8 3D acc calibration, H20,H20H headless calib
|
||||
H8_3D_FLAG_CALIBRATE2 = 0x10, // H11D, H20, H20H acc calibration
|
||||
H8_3D_FLAG_CAM_DN = 0x08,
|
||||
H8_3D_FLAG_CAM_UP = 0x04,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) H8_3D_send_packet()
|
||||
{
|
||||
if(sub_protocol==H20H)
|
||||
packet[0] = 0x14;
|
||||
else // H8_3D, H20MINI, H30MINI
|
||||
packet[0] = 0x13;
|
||||
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
packet[8] = rx_tx_addr[0]+rx_tx_addr[1]+rx_tx_addr[2]+rx_tx_addr[3]; // txid checksum
|
||||
memset(&packet[9], 0, 10);
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[5] = 0x00;
|
||||
packet[6] = 0x00;
|
||||
packet[7] = 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[5] = hopping_frequency_no;
|
||||
packet[7] = 0x03;
|
||||
|
||||
rudder = convert_channel_16b_limit(RUDDER,0x44,0xBC); // yaw right : 0x80 (neutral) - 0xBC (right)
|
||||
if(sub_protocol!=H20H)
|
||||
{ // H8_3D, H20MINI, H30MINI
|
||||
packet[6] = 0x08;
|
||||
packet[9] = convert_channel_8b(THROTTLE); // throttle : 0x00 - 0xFF
|
||||
packet[15] = 0x20; // trims
|
||||
packet[16] = 0x20; // trims
|
||||
if (rudder<=0x80)
|
||||
rudder=0x80-rudder; // yaw left : 0x00 (neutral) - 0x3C (left)
|
||||
if(rudder==0x01 || rudder==0x81)
|
||||
rudder=0x00; // Small deadband
|
||||
}
|
||||
else
|
||||
{ //H20H
|
||||
packet[6] = hopping_frequency_no == 0 ? 8 - packet_count : 16 - packet_count;
|
||||
packet[9] = convert_channel_16b_limit(THROTTLE, 0x43, 0xBB); // throttle : 0x43 - 0x7F - 0xBB
|
||||
packet[15]= 0x40; // trims
|
||||
packet[16]= 0x40; // trims
|
||||
rudder--; // rudder : 0x43 - 0x7F - 0xBB
|
||||
if (rudder>=0x7F-1 && rudder<=0x7F+1)
|
||||
rudder=0x7F; // Small deadband
|
||||
}
|
||||
packet[10] = rudder;
|
||||
packet[11] = convert_channel_16b_limit(ELEVATOR, 0x43, 0xBB); // elevator : 0x43 - 0x7F - 0xBB
|
||||
packet[12] = convert_channel_16b_limit(AILERON, 0x43, 0xBB); // aileron : 0x43 - 0x7F - 0xBB
|
||||
// neutral trims
|
||||
packet[13] = 0x20;
|
||||
packet[14] = 0x20;
|
||||
// flags
|
||||
packet[17] = H8_3D_FLAG_RATE_HIGH
|
||||
| GET_FLAG(CH5_SW,H8_3D_FLAG_FLIP)
|
||||
| GET_FLAG(CH6_SW,H8_3D_FLAG_LIGTH) //H22 light
|
||||
| GET_FLAG(CH9_SW,H8_3D_FLAG_HEADLESS)
|
||||
| GET_FLAG(CH10_SW,H8_3D_FLAG_RTH); // 180/360 flip mode on H8 3D
|
||||
packet[18] = GET_FLAG(CH7_SW,H8_3D_FLAG_PICTURE)
|
||||
| GET_FLAG(CH8_SW,H8_3D_FLAG_VIDEO)
|
||||
| GET_FLAG(CH11_SW,H8_3D_FLAG_CALIBRATE1)
|
||||
| GET_FLAG(CH12_SW,H8_3D_FLAG_CALIBRATE2);
|
||||
if(Channel_data[CH13]<CHANNEL_MIN_COMMAND)
|
||||
packet[18] |= H8_3D_FLAG_CAM_DN;
|
||||
if(CH13_SW)
|
||||
packet[18] |= H8_3D_FLAG_CAM_UP;
|
||||
}
|
||||
uint8_t sum = packet[9];
|
||||
for (uint8_t i=10; i < H8_3D_PACKET_SIZE-1; i++)
|
||||
sum += packet[i];
|
||||
packet[19] = sum; // data checksum
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
if(sub_protocol!=H20H)
|
||||
{ // H8_3D, H20MINI, H30MINI
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? hopping_frequency[0] : hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= H8_3D_RF_NUM_CHANNELS;
|
||||
}
|
||||
else
|
||||
{ //H20H
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? H20H_BIND_RF : hopping_frequency[packet_count>>3]);
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
packet_count++;
|
||||
if(packet_count>15)
|
||||
{
|
||||
packet_count = 0;
|
||||
hopping_frequency_no = 0;
|
||||
}
|
||||
else
|
||||
if(packet_count > 7)
|
||||
hopping_frequency_no = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, H8_3D_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) H8_3D_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if(sub_protocol==H20H)
|
||||
XN297_SetTXAddr((uint8_t *)"\xEE\xDD\xCC\xBB\x11", 5);
|
||||
else // H8_3D, H20MINI, H30MINI
|
||||
XN297_SetTXAddr((uint8_t *)"\xC4\x57\x09\x65\x21", 5);
|
||||
}
|
||||
|
||||
uint16_t H8_3D_callback()
|
||||
{
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
BIND_DONE;
|
||||
packet_count=0;
|
||||
}
|
||||
}
|
||||
#ifdef MULTI_SYNC
|
||||
else
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
|
||||
H8_3D_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
// captured from H20H stock transmitters
|
||||
const uint8_t PROGMEM h20h_tx_rf_map[3][6] = {{/*ID*/0x83, 0x3c, 0x60, 0x00, /*RF*/0x47, 0x3e},
|
||||
{/*ID*/0x5c, 0x2b, 0x60, 0x00, /*RF*/0x4a, 0x3c},
|
||||
{/*ID*/0x57, 0x07, 0x00, 0x00, /*RF*/0x41, 0x48} };
|
||||
// captured from H20 Mini / H30 Mini stock transmitters
|
||||
const uint8_t PROGMEM h20mini_tx_rf_map[4][8] = {{/*ID*/0xb4, 0xbb, 0x09, 0x00, /*RF*/0x3e, 0x45, 0x47, 0x4a},
|
||||
{/*ID*/0x94, 0x9d, 0x0b, 0x00, /*RF*/0x3e, 0x43, 0x49, 0x4a},
|
||||
{/*ID*/0xd1, 0xd0, 0x00, 0x00, /*RF*/0x3f, 0x42, 0x46, 0x4a},
|
||||
{/*ID*/0xcb, 0xcd, 0x04, 0x00, /*RF*/0x41, 0x43, 0x46, 0x4a}};
|
||||
static void __attribute__((unused)) H8_3D_initialize_txid()
|
||||
{
|
||||
uint8_t id_num=rx_tx_addr[4];
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case H8_3D:
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
hopping_frequency[i] = 6 + (0x0f*i) + (((rx_tx_addr[i] >> 4) + (rx_tx_addr[i] & 0x0f)) % 0x0f);
|
||||
break;
|
||||
case H20H:
|
||||
id_num%=3; // 3 different IDs
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
{
|
||||
rx_tx_addr[i] = pgm_read_byte_near(&h20h_tx_rf_map[id_num][i]);
|
||||
if(i<2)
|
||||
hopping_frequency[i] = pgm_read_byte_near(&h20h_tx_rf_map[id_num][i+4]);
|
||||
}
|
||||
break;
|
||||
case H20MINI:
|
||||
case H30MINI:
|
||||
id_num%=4; // 4 different IDs
|
||||
for(uint8_t i=0; i<4; i++)
|
||||
{
|
||||
rx_tx_addr[i] = pgm_read_byte_near(&h20mini_tx_rf_map[id_num][i]);
|
||||
hopping_frequency[i] = pgm_read_byte_near(&h20mini_tx_rf_map[id_num][i+4]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void H8_3D_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = H8_3D_BIND_COUNT;
|
||||
H8_3D_initialize_txid();
|
||||
H8_3D_RF_init();
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case H8_3D:
|
||||
packet_period=H8_3D_PACKET_PERIOD;
|
||||
break;
|
||||
case H20H:
|
||||
packet_period=H20H_PACKET_PERIOD;
|
||||
break;
|
||||
case H20MINI:
|
||||
case H30MINI:
|
||||
packet_period=H20MINI_PACKET_PERIOD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
532
Multiprotocol/HOTT_cc2500.ino
Normal file
532
Multiprotocol/HOTT_cc2500.ino
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(HOTT_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define HOTT_FORCE_ID // Force ID of original dump
|
||||
|
||||
#define HOTT_TX_PACKET_LEN 50
|
||||
#define HOTT_RX_PACKET_LEN 22
|
||||
#define HOTT_PACKET_PERIOD 10000
|
||||
#define HOTT_NUM_RF_CHANNELS 75
|
||||
#define HOTT_COARSE 0
|
||||
|
||||
enum {
|
||||
HOTT_START = 0x00,
|
||||
HOTT_CAL = 0x01,
|
||||
HOTT_DATA1 = 0x02,
|
||||
HOTT_DATA2 = 0x03,
|
||||
HOTT_RX1 = 0x04,
|
||||
HOTT_RX2 = 0x05,
|
||||
};
|
||||
|
||||
#ifdef HOTT_FW_TELEMETRY
|
||||
#define HOTT_SENSOR_TYPE 6
|
||||
#define HOTT_SENSOR_SEARCH_PERIOD 2000
|
||||
uint8_t HOTT_sensor_cur=0;
|
||||
uint8_t HOTT_sensor_pages=0;
|
||||
uint8_t HOTT_sensor_valid=false;
|
||||
uint8_t HOTT_sensor_ok[HOTT_SENSOR_TYPE];
|
||||
uint8_t HOTT_sensor_seq=0;
|
||||
#endif
|
||||
|
||||
#define HOTT_FREQ0_VAL 0x6E
|
||||
|
||||
// Some important initialization parameters, all others are either default,
|
||||
// or not important in the context of transmitter
|
||||
// FIFOTHR 00
|
||||
// SYNC1 D3
|
||||
// SYNC0 91
|
||||
// PKTLEN 32 - Packet length, 50 bytes
|
||||
// PKTCTRL1 04 - APPEND_STATUS on=RSSI+LQI, all other are receive parameters - irrelevant
|
||||
// PKTCTRL0 44 - whitening, use FIFO, use CRC, fixed packet length
|
||||
// ADDR 00
|
||||
// CHANNR 10
|
||||
// FSCTRL1 09 - IF
|
||||
// FSCTRL0 00 - zero freq offset
|
||||
// FREQ2 5C - synthesizer frequencyfor 26MHz crystal
|
||||
// FREQ1 6C
|
||||
// FREQ0 B9
|
||||
// MDMCFG4 2D -
|
||||
// MDMCFG3 3B -
|
||||
// MDMCFG2 73 - disable DC blocking, MSK, no Manchester code, 32 bits sync word
|
||||
// MDMCFG1 A3 - FEC enable, 4 preamble bytes, CHANSPC_E - 03
|
||||
// MDMCFG0 AA - CHANSPC_M - AA
|
||||
// DEVIATN 47 -
|
||||
// MCSM2 07 -
|
||||
// MCSM1 00 - always use CCA, go to IDLE when done
|
||||
// MCSM0 08 - disable autocalibration, PO_TIMEOUT - 64, no pin radio control, no forcing XTAL to stay in SLEEP
|
||||
// FOCCFG 1D
|
||||
const PROGMEM uint8_t HOTT_init_values[] = {
|
||||
/* 00 */ 0x2F, 0x2E, 0x2F, 0x00, 0xD3, 0x91, 0x32, 0x04,
|
||||
/* 08 */ 0x44, 0x00, 0x00, 0x09, 0x00, 0x5C, 0x6C, HOTT_FREQ0_VAL + HOTT_COARSE,
|
||||
/* 10 */ 0x2D, 0x3B, 0x73, 0xA3, 0xAA, 0x47, 0x07, 0x00,
|
||||
/* 18 */ 0x08, 0x1D, 0x1C, 0xC7, 0x09, 0xF0, 0x87, 0x6B,
|
||||
/* 20 */ 0xF0, 0xB6, 0x10, 0xEA, 0x0A, 0x00, 0x11
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) HOTT_rf_init()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
for (uint8_t i = 0; i < 39; ++i)
|
||||
CC2500_WriteReg(i, pgm_read_byte_near(&HOTT_init_values[i]));
|
||||
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HOTT_tune_chan()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, (rf_ch_num+1)*3);
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HOTT_tune_chan_fast()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, (rf_ch_num+1)*3);
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[rf_ch_num]);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HOTT_tune_freq()
|
||||
{
|
||||
if ( prev_option != option )
|
||||
{
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
CC2500_WriteReg(CC2500_0F_FREQ0, HOTT_FREQ0_VAL + HOTT_COARSE);
|
||||
prev_option = option ;
|
||||
phase = HOTT_START; // Restart the tune process if option is changed to get good tuned values
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM HOTT_hop[][HOTT_NUM_RF_CHANNELS]=
|
||||
{ { 48, 37, 16, 62, 9, 50, 42, 22, 68, 0, 55, 35, 21, 74, 1, 56, 31, 20, 70, 11, 45, 32, 24, 71, 8, 54, 38, 26, 61, 13, 53, 30, 15, 65, 7, 52, 34, 28, 60, 3, 47, 39, 18, 69, 2, 49, 44, 23, 72, 5, 51, 43, 19, 64, 12, 46, 33, 17, 67, 6, 58, 36, 29, 73, 14, 57, 41, 25, 63, 4, 59, 40, 27, 66, 10 },
|
||||
{ 50, 23, 5, 34, 67, 53, 22, 12, 39, 62, 51, 21, 10, 33, 63, 59, 16, 1, 43, 66, 49, 19, 8, 30, 71, 47, 24, 2, 35, 68, 45, 25, 14, 41, 74, 55, 18, 4, 32, 61, 54, 17, 11, 31, 72, 52, 28, 6, 38, 65, 46, 15, 9, 40, 60, 48, 26, 3, 37, 70, 58, 29, 0, 36, 64, 56, 20, 7, 42, 69, 57, 27, 13, 44, 73 },
|
||||
{ 73, 51, 39, 18, 9, 64, 56, 34, 16, 12, 66, 58, 36, 25, 11, 61, 47, 40, 15, 8, 71, 50, 43, 20, 6, 62, 54, 42, 19, 3, 63, 46, 44, 29, 14, 72, 49, 33, 22, 5, 69, 57, 30, 21, 10, 70, 45, 35, 26, 7, 65, 59, 31, 28, 1, 67, 48, 32, 24, 0, 60, 55, 41, 17, 2, 74, 52, 38, 27, 4, 68, 53, 37, 23, 13 },
|
||||
{ 52, 60, 40, 21, 14, 50, 72, 41, 23, 13, 59, 61, 39, 16, 6, 58, 66, 33, 17, 5, 55, 64, 43, 20, 12, 54, 74, 35, 29, 3, 46, 63, 37, 22, 10, 48, 65, 31, 27, 9, 49, 73, 38, 24, 11, 56, 70, 32, 15, 1, 51, 71, 44, 18, 8, 45, 67, 36, 25, 7, 57, 62, 34, 28, 2, 53, 69, 42, 19, 4, 47, 68, 30, 26, 0 },
|
||||
{ 50, 16, 34, 6, 71, 51, 24, 40, 7, 68, 57, 27, 33, 14, 70, 55, 26, 30, 5, 74, 47, 28, 44, 11, 67, 49, 15, 32, 9, 61, 52, 22, 37, 13, 66, 59, 18, 42, 3, 62, 46, 29, 31, 12, 60, 48, 19, 38, 1, 72, 58, 17, 36, 4, 64, 53, 21, 39, 0, 63, 56, 20, 41, 2, 65, 45, 25, 35, 10, 69, 54, 23, 43, 8, 73 },
|
||||
{ 55, 38, 12, 62, 23, 52, 44, 3, 66, 18, 54, 36, 10, 74, 16, 56, 42, 9, 70, 17, 58, 33, 5, 69, 20, 50, 40, 1, 63, 24, 53, 37, 13, 65, 15, 48, 34, 4, 61, 22, 57, 31, 6, 64, 26, 46, 35, 11, 72, 21, 47, 30, 7, 68, 29, 45, 32, 8, 60, 19, 49, 43, 2, 67, 27, 51, 39, 0, 71, 28, 59, 41, 14, 73, 25 },
|
||||
{ 70, 32, 18, 10, 58, 69, 38, 22, 2, 54, 67, 36, 19, 12, 57, 62, 34, 20, 14, 52, 63, 41, 15, 3, 51, 73, 42, 28, 6, 48, 60, 43, 29, 5, 45, 64, 31, 17, 4, 56, 65, 35, 26, 13, 53, 61, 37, 23, 1, 49, 68, 40, 16, 9, 47, 71, 39, 25, 7, 50, 66, 33, 24, 8, 59, 72, 44, 27, 11, 46, 74, 30, 21, 0, 55 },
|
||||
{ 6, 45, 71, 27, 44, 10, 46, 74, 22, 32, 0, 55, 69, 21, 33, 4, 50, 66, 18, 38, 7, 57, 62, 19, 36, 1, 48, 70, 20, 40, 8, 47, 68, 15, 43, 2, 58, 61, 26, 42, 3, 56, 72, 23, 34, 14, 54, 67, 16, 37, 5, 59, 64, 24, 30, 12, 52, 65, 25, 39, 13, 49, 73, 17, 31, 9, 53, 60, 28, 35, 11, 51, 63, 29, 41 },
|
||||
{ 31, 65, 50, 20, 13, 37, 66, 45, 23, 5, 32, 69, 54, 19, 7, 39, 74, 52, 27, 1, 42, 64, 53, 22, 4, 43, 70, 58, 16, 3, 40, 71, 57, 17, 0, 35, 63, 56, 18, 9, 44, 72, 51, 21, 6, 33, 67, 46, 25, 11, 30, 73, 55, 15, 8, 36, 62, 48, 24, 10, 41, 60, 49, 29, 14, 34, 61, 47, 26, 2, 38, 68, 59, 28, 12 },
|
||||
{ 67, 22, 49, 36, 13, 64, 28, 57, 37, 6, 65, 29, 46, 39, 3, 70, 26, 45, 35, 1, 62, 24, 58, 34, 10, 68, 19, 53, 33, 4, 66, 21, 52, 31, 7, 74, 18, 47, 32, 5, 61, 16, 51, 38, 8, 72, 23, 55, 30, 12, 73, 17, 59, 44, 0, 60, 15, 50, 43, 14, 63, 27, 48, 42, 11, 71, 20, 54, 41, 9, 69, 25, 56, 40, 2 },
|
||||
{ 19, 38, 14, 66, 57, 18, 44, 7, 74, 48, 23, 30, 6, 71, 58, 26, 32, 5, 61, 46, 20, 34, 0, 68, 45, 24, 36, 1, 70, 50, 27, 33, 10, 63, 52, 16, 42, 9, 65, 51, 15, 41, 11, 64, 53, 22, 37, 3, 60, 56, 28, 35, 4, 67, 49, 17, 39, 13, 69, 54, 25, 43, 2, 73, 55, 21, 31, 8, 62, 47, 29, 40, 12, 72, 59 },
|
||||
{ 4, 52, 64, 28, 44, 14, 46, 74, 16, 32, 11, 50, 68, 27, 36, 0, 47, 70, 26, 34, 13, 57, 61, 18, 38, 6, 56, 62, 19, 40, 5, 58, 67, 17, 31, 12, 54, 63, 22, 33, 3, 53, 72, 21, 41, 10, 48, 66, 15, 35, 7, 45, 60, 20, 37, 9, 51, 69, 25, 42, 2, 59, 71, 24, 39, 1, 55, 65, 23, 30, 8, 49, 73, 29, 43 },
|
||||
{ 44, 66, 19, 1, 56, 35, 62, 20, 4, 54, 39, 70, 24, 5, 55, 31, 74, 26, 12, 58, 32, 60, 17, 10, 45, 37, 63, 22, 3, 50, 33, 64, 16, 7, 51, 34, 61, 21, 8, 48, 38, 68, 29, 0, 46, 36, 72, 28, 14, 49, 42, 69, 25, 6, 57, 43, 65, 18, 2, 52, 30, 71, 23, 13, 47, 41, 67, 15, 9, 53, 40, 73, 27, 11, 59 },
|
||||
{ 12, 16, 36, 46, 69, 6, 20, 44, 58, 62, 11, 19, 34, 48, 71, 1, 18, 42, 50, 74, 3, 25, 31, 47, 65, 0, 24, 33, 45, 72, 2, 23, 35, 56, 64, 10, 22, 38, 49, 63, 7, 26, 37, 51, 70, 14, 21, 30, 53, 67, 5, 15, 40, 52, 66, 9, 17, 39, 55, 60, 13, 27, 41, 54, 73, 4, 28, 32, 57, 61, 8, 29, 43, 59, 68 },
|
||||
{ 63, 42, 18, 2, 57, 71, 34, 22, 10, 48, 67, 36, 25, 4, 46, 60, 31, 28, 6, 47, 74, 37, 15, 0, 55, 65, 32, 24, 12, 56, 66, 40, 27, 14, 52, 62, 38, 19, 3, 50, 73, 33, 29, 11, 53, 61, 35, 16, 7, 58, 72, 41, 26, 5, 59, 69, 30, 20, 9, 51, 68, 44, 23, 1, 49, 70, 39, 17, 8, 54, 64, 43, 21, 13, 45 },
|
||||
{ 52, 1, 71, 17, 36, 47, 7, 64, 26, 32, 53, 5, 60, 20, 42, 57, 2, 66, 18, 34, 56, 4, 63, 24, 35, 46, 13, 72, 22, 30, 48, 0, 67, 21, 39, 50, 3, 74, 16, 31, 59, 14, 61, 23, 37, 45, 6, 65, 19, 44, 51, 11, 62, 27, 41, 55, 9, 68, 15, 38, 58, 8, 70, 29, 40, 54, 10, 69, 28, 33, 49, 12, 73, 25, 43 }
|
||||
};
|
||||
const uint16_t PROGMEM HOTT_hop_val[] = { 0xC06B, 0xC34A, 0xDB24, 0x8E09, 0x272E, 0x217F, 0x155B, 0xEDE8, 0x1D31, 0x0986, 0x56F7, 0x6454, 0xC42D, 0x01D2, 0xC253, 0x1180 };
|
||||
|
||||
static void __attribute__((unused)) HOTT_TXID_init()
|
||||
{
|
||||
packet[0] = pgm_read_word_near( &HOTT_hop_val[num_ch] );
|
||||
packet[1] = pgm_read_word_near( &HOTT_hop_val[num_ch] )>>8;
|
||||
|
||||
for(uint8_t i=0; i<HOTT_NUM_RF_CHANNELS; i++)
|
||||
hopping_frequency[i]=pgm_read_byte_near( &HOTT_hop[num_ch][i] );
|
||||
#ifdef HOTT_FORCE_ID
|
||||
memcpy(rx_tx_addr,"\x7C\x94\x00\x0D\x50",5); //TX1
|
||||
memcpy(rx_tx_addr,"\xEA\x4D\x00\x01\x50",5); //TX2
|
||||
#endif
|
||||
memset(&packet[30],0xFF,9);
|
||||
packet[39]=0x07; // unknown and constant
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
memset(&packet[40],0xFA,5);
|
||||
memcpy(&packet[45],rx_tx_addr,5);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&packet[40],rx_tx_addr,5);
|
||||
uint16_t addr=HOTT_EEPROM_OFFSET+RX_num*5;
|
||||
debug("RXID: ");
|
||||
for(uint8_t i=0;i<5;i++)
|
||||
{
|
||||
packet[45+i]=eeprom_read_byte((EE_ADDR)(addr+i));
|
||||
debug(" %02X",packet[45+i]);
|
||||
}
|
||||
debugln("");
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HOTT_prep_data_packet()
|
||||
{
|
||||
static uint8_t upper=0;
|
||||
|
||||
packet[2] = hopping_frequency_no;
|
||||
|
||||
packet[3] = upper; // used for failsafe and upper channels (only supporting 16 channels)
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
static uint8_t failsafe_count=0;
|
||||
if(IS_FAILSAFE_VALUES_on && IS_BIND_DONE)
|
||||
{
|
||||
failsafe_count++;
|
||||
if(failsafe_count>=3)
|
||||
{
|
||||
FAILSAFE_VALUES_off;
|
||||
failsafe_count=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
failsafe_count=0;
|
||||
#endif
|
||||
|
||||
// Channels value are PPM*2, -100%=1100µs, +100%=1900µs, order TAER
|
||||
uint16_t val;
|
||||
for(uint8_t i=4;i<28;i+=2)
|
||||
{
|
||||
uint8_t ch=(i-4)>>1;
|
||||
if(upper && ch >= 8)
|
||||
ch+=4; // when upper swap CH9..CH12 by CH13..16
|
||||
val=Channel_data[ch];
|
||||
val=(((val<<2)+val)>>2)+860*2; // value range 860<->2140 *2 <-> -125%<->+125%
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(failsafe_count==1)
|
||||
{ // first failsafe packet
|
||||
packet[3] |= 0x40;
|
||||
uint16_t fs=Failsafe_data[ch];
|
||||
if( fs == FAILSAFE_CHANNEL_HOLD || fs == FAILSAFE_CHANNEL_NOPULSES)
|
||||
val|=0x8000; // channel hold flag
|
||||
else
|
||||
{
|
||||
val=(((fs<<2)+fs)>>2)+860*2; // value range 860<->2140 *2 <-> -125%<->+125%
|
||||
val|=0x4000; // channel specific position flag
|
||||
}
|
||||
}
|
||||
else if(failsafe_count==2)
|
||||
{ // second failsafe packet=timing?
|
||||
packet[3] |= 0x50;
|
||||
if(i==4)
|
||||
val=2;
|
||||
else
|
||||
val=0;
|
||||
}
|
||||
#endif
|
||||
packet[i] = val;
|
||||
packet[i+1] = val>>8;
|
||||
}
|
||||
upper ^= 0x01; // toggle between CH9..CH12 and CH13..16
|
||||
|
||||
#ifdef HOTT_FW_TELEMETRY
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
static uint8_t prev_SerialRX_val=0;
|
||||
if(HoTT_SerialRX)
|
||||
{//Text mode
|
||||
uint8_t sensor=HoTT_SerialRX_val&0xF0;
|
||||
if((sensor&0x80) && sensor!=0xF0 && (HoTT_SerialRX_val&0x0F) >= 0x07)
|
||||
{//Valid Text query
|
||||
if(sensor==0x80) HoTT_SerialRX_val&=0x0F; // RX only
|
||||
if(prev_SerialRX_val!=HoTT_SerialRX_val)
|
||||
{
|
||||
prev_SerialRX_val=HoTT_SerialRX_val;
|
||||
packet[28] = HoTT_SerialRX_val; // send the button being pressed only once
|
||||
}
|
||||
else
|
||||
packet[28] = HoTT_SerialRX_val | 0x0F; // no button pressed
|
||||
packet[29] = 0x01; // 0x01->Text config menu
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[28] = 0x89+HOTT_sensor_cur; // 0x89/8A/8B/8C/8D/8E during normal packets
|
||||
if(sub_protocol == HOTT_SYNC)
|
||||
packet[29] = ((HOTT_sensor_seq+1)<<3) | 2; // Telemetry packet sequence
|
||||
else
|
||||
packet[29] = 0x02;
|
||||
//debugln("28=%02X,29=%02X",packet[28],packet[29]);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
packet[28] = 0x80; // no sensor
|
||||
packet[29] = 0x02; // unknown 0x02 when bind starts then when RX replies cycle in sequence 0x1A/22/2A/0A/12, 0x02 during normal packets, 0x01->text config menu, 0x0A->no more RX telemetry
|
||||
}
|
||||
|
||||
CC2500_WriteReg(CC2500_06_PKTLEN, HOTT_TX_PACKET_LEN);
|
||||
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, packet, HOTT_TX_PACKET_LEN);
|
||||
#if 0
|
||||
debug("RF:%02X P:",rf_ch_num);
|
||||
for(uint8_t i=0;i<HOTT_TX_PACKET_LEN;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no %= HOTT_NUM_RF_CHANNELS;
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no];
|
||||
}
|
||||
|
||||
uint16_t HOTT_callback()
|
||||
{
|
||||
switch(phase)
|
||||
{
|
||||
case HOTT_START:
|
||||
rf_ch_num = 0;
|
||||
HOTT_tune_chan();
|
||||
phase = HOTT_CAL;
|
||||
return 2000;
|
||||
case HOTT_CAL:
|
||||
calData[rf_ch_num]=CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
if (++rf_ch_num < HOTT_NUM_RF_CHANNELS)
|
||||
HOTT_tune_chan();
|
||||
else
|
||||
{
|
||||
hopping_frequency_no = 0;
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no];
|
||||
counter = 0;
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
phase = HOTT_DATA1;
|
||||
}
|
||||
return 2000;
|
||||
|
||||
/* Work cycle: 10ms */
|
||||
case HOTT_DATA1:
|
||||
//Set RF freq, setup LBT and prep packet
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(HOTT_PACKET_PERIOD);
|
||||
#endif
|
||||
//Clear all
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SNOP);
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
CC2500_WriteReg(CC2500_04_SYNC1, 0xD3);
|
||||
CC2500_WriteReg(CC2500_05_SYNC0, 0x91);
|
||||
//Set RF freq
|
||||
HOTT_tune_freq();
|
||||
HOTT_tune_chan_fast();
|
||||
//Setup LBT
|
||||
CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0xFF);
|
||||
CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0x0C);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
//Prep packet
|
||||
HOTT_prep_data_packet();
|
||||
//Listen
|
||||
CC2500_WriteReg(CC2500_17_MCSM1, 0x10); //??
|
||||
CC2500_WriteReg(CC2500_18_MCSM0, 0x18); //??
|
||||
CC2500_Strobe(CC2500_SRX); //??
|
||||
phase++; //HOTT_DATA2
|
||||
return 1095;
|
||||
case HOTT_DATA2:
|
||||
//LBT
|
||||
if((CC2500_ReadReg(CC2500_38_PKTSTATUS | CC2500_READ_BURST)&0x10)==0)
|
||||
{ //Channel is busy
|
||||
LBT_POWER_on; // Reduce to low power before transmitting
|
||||
debugln("Busy %d",rf_ch_num);
|
||||
}
|
||||
CC2500_WriteReg(CC2500_17_MCSM1, 0x00); //??
|
||||
CC2500_WriteReg(CC2500_18_MCSM0, 0x08); //??
|
||||
CC2500_SetPower();
|
||||
//Send packet
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
phase++; //HOTT_RX1
|
||||
return 3880;
|
||||
case HOTT_RX1:
|
||||
//Clear all
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
CC2500_Strobe(CC2500_SFRX);
|
||||
//RX
|
||||
if(packet[29] & 0xF8)
|
||||
{// binary telemetry
|
||||
CC2500_WriteReg(CC2500_04_SYNC1, 0x2C);
|
||||
CC2500_WriteReg(CC2500_05_SYNC0, 0x6E);
|
||||
}
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0xC7);
|
||||
CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0x09);
|
||||
CC2500_WriteReg(CC2500_06_PKTLEN, HOTT_RX_PACKET_LEN);
|
||||
CC2500_Strobe(CC2500_SRX);
|
||||
phase++; //HOTT_RX2
|
||||
return 4025;
|
||||
case HOTT_RX2:
|
||||
//Telemetry
|
||||
len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if (len==HOTT_RX_PACKET_LEN+2)
|
||||
{
|
||||
CC2500_ReadData(packet_in, len);
|
||||
if((packet_in[HOTT_RX_PACKET_LEN+1]&0x80) && memcmp(rx_tx_addr,packet_in,5)==0)
|
||||
{ // CRC OK and TX ID matches
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
//GR-16: D4 20 F2 E6 F6 31 BD 01 00 90 00 FF 03 00 9E 1B 00 00 00 00 00 00
|
||||
//GR-12L: D4 20 F2 E6 F6 6E EE 01 00 B1 00 FF 03 00 0E 08 10 00 02 00 00 00
|
||||
//Vector: D4 20 F2 E6 F6 00 00 3A 01 A1 00 00 1A 24 35 1A 00 24 00 00 00 1A
|
||||
// -----TXID----- -----RXID----- ---------------Unknown-------------
|
||||
debug("B:");
|
||||
for(uint8_t i=0;i<HOTT_RX_PACKET_LEN;i++)
|
||||
debug(" %02X", packet_in[i]);
|
||||
debugln("");
|
||||
uint16_t addr=HOTT_EEPROM_OFFSET+RX_num*5;
|
||||
for(uint8_t i=0; i<5; i++)
|
||||
eeprom_write_byte((EE_ADDR)(addr+i),packet_in[5+i]);
|
||||
BIND_DONE;
|
||||
HOTT_TXID_init();
|
||||
}
|
||||
#ifdef HOTT_FW_TELEMETRY
|
||||
else
|
||||
{ //Telemetry
|
||||
// [0..4] = TXID
|
||||
// [5..9] = RXID
|
||||
// [10] = 0x40 bind, 0x00 normal, 0x80 config menu
|
||||
// [11] = telmetry pages. For sensors 0x00 to 0x04, for config mennu 0x00 to 0x12.
|
||||
// Normal telem page 0 = 0x55, 0x32, 0x38, 0x55, 0x64, 0x32, 0xD0, 0x07, 0x00, 0x55
|
||||
// Page 0 [12] = [21] = [15]
|
||||
// Page 0 [13] = RX_Voltage Cur*10 in V
|
||||
// Page 0 [14] = Temperature-20 in °C
|
||||
// Page 0 [15] = RX_RSSI CC2500 formated (a<128:a/2-71dBm, a>=128:(a-256)/2-71dBm)
|
||||
// Page 0 [16] = RX_LQI in %
|
||||
// Page 0 [17] = RX_Voltage Min*10 in V
|
||||
// Page 0 [18,19] = [19]<<8+[18]=max lost packet time in ms, max value seems 2s=0x7D0
|
||||
// Page 0 [20] = 0x00 ??
|
||||
//
|
||||
// Config menu consists of the different telem pages put all together
|
||||
// Page X [12] = seems like all the telem pages with the same value are going together to make the full config menu text. Seen so far 'a', 'b', 'c', 'd'
|
||||
// Page X [13..21] = 9 ascii chars to be displayed, char is highlighted when ascii|0x80
|
||||
// Screen display is 21 characters large which means that once the first 21 chars are filled go to the begining of the next line
|
||||
// Menu commands are sent through TX packets:
|
||||
// packet[28]= 0xXF=>no key press, 0xXD=>down, 0xXB=>up, 0xX9=>enter, 0xXE=>right, 0xX7=>left with X=0 or D
|
||||
// packet[29]= 0xX1/0xX9 with X=0 or X counting 0,1,1,2,2,..,9,9
|
||||
// Reduce telemetry to 14 bytes
|
||||
packet_in[0]= packet_in[HOTT_RX_PACKET_LEN];
|
||||
packet_in[1]= TX_LQI;
|
||||
bool send_telem=true;
|
||||
if(packet[29]==1)
|
||||
{ //Text mode
|
||||
HOTT_sensor_pages = 0;
|
||||
HOTT_sensor_valid = false;
|
||||
packet_in[10] = 0x80; // Marking telem Text mode
|
||||
packet_in[12] = 0;
|
||||
for(uint8_t i=0; i<HOTT_SENSOR_TYPE;i++)
|
||||
packet_in[12] |= HOTT_sensor_ok[i]<<i; // Send detected sensors
|
||||
}
|
||||
else
|
||||
{ //Binary sensor
|
||||
HOTT_sensor_seq++; // Increment RX sequence counter
|
||||
HOTT_sensor_seq %= 5; // 5 pages in binary mode per sensor
|
||||
if(state==0 && HOTT_sensor_ok[0]==false && HOTT_sensor_ok[1]==false && HOTT_sensor_ok[2]==false && HOTT_sensor_ok[3]==false && HOTT_sensor_ok[4]==false && HOTT_sensor_ok[5]==false)
|
||||
HOTT_sensor_seq=0; // No sensors always ask page 0
|
||||
if(state)
|
||||
state--;
|
||||
if( packet_in[11]==1 ) // Page 1
|
||||
{
|
||||
if( packet_in[12] == (HOTT_sensor_cur+9)<<4 )
|
||||
{ //Requested sensor is sending: 0x90/A0/B0/C0/D0/E0
|
||||
HOTT_sensor_pages = 0; // Sensor first page received
|
||||
HOTT_sensor_valid = true; // Data from the expected sensor is being received
|
||||
HOTT_sensor_ok[(packet_in[12]>>4)-9]=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
HOTT_sensor_valid = false;
|
||||
HOTT_sensor_pages = 0x1E; // Switch to next sensor
|
||||
}
|
||||
}
|
||||
if(packet_in[11])
|
||||
{ //Page != 0
|
||||
if(HOTT_sensor_valid) // Valid
|
||||
{
|
||||
packet_in[10] = HOTT_sensor_cur+9; // Mark telem with sensor ID
|
||||
HOTT_sensor_pages |= 1<<packet_in[11]; // Page received
|
||||
}
|
||||
else
|
||||
send_telem=false; // Do not send
|
||||
}
|
||||
else
|
||||
packet_in[10]=0; // Mark telem with sensor 0=RX
|
||||
}
|
||||
debug("T%d=",send_telem);
|
||||
for(uint8_t i=10;i < HOTT_RX_PACKET_LEN; i++)
|
||||
{
|
||||
packet_in[i-8]=packet_in[i];
|
||||
debug(" %02X",packet_in[i]);
|
||||
}
|
||||
debugln("");
|
||||
if(send_telem)
|
||||
telemetry_link=2;
|
||||
if((HOTT_sensor_pages&0x1E) == 0x1E) // All 4 pages received from the sensor
|
||||
{ //Next sensor
|
||||
uint8_t loop=0;
|
||||
do
|
||||
{
|
||||
HOTT_sensor_cur++; // Switch to next sensor
|
||||
HOTT_sensor_cur %= HOTT_SENSOR_TYPE;
|
||||
loop++;
|
||||
}
|
||||
while(HOTT_sensor_ok[HOTT_sensor_cur]==false && loop<HOTT_SENSOR_TYPE+1 && state==0);
|
||||
HOTT_sensor_valid=false;
|
||||
HOTT_sensor_pages=0;
|
||||
HOTT_sensor_seq=0;
|
||||
debugln("Sensor:%02X",HOTT_sensor_cur+9);
|
||||
}
|
||||
}
|
||||
pps_counter++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef HOTT_FW_TELEMETRY
|
||||
packet_count++;
|
||||
if(packet_count>=100)
|
||||
{
|
||||
TX_LQI=pps_counter;
|
||||
if(pps_counter==0)
|
||||
{ // lost connection with RX, power cycle? research sensors again.
|
||||
HOTT_sensor_cur=3;
|
||||
HOTT_sensor_seq=0;
|
||||
HOTT_sensor_valid=false;
|
||||
for(uint8_t i=0; i<HOTT_SENSOR_TYPE;i++)
|
||||
HOTT_sensor_ok[i]=false; // no sensors detected
|
||||
state=HOTT_SENSOR_SEARCH_PERIOD;
|
||||
}
|
||||
pps_counter=packet_count=0;
|
||||
}
|
||||
#endif
|
||||
phase=HOTT_DATA1;
|
||||
return 1000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HOTT_init()
|
||||
{
|
||||
num_ch=random(0xfefefefe)%16;
|
||||
HOTT_TXID_init();
|
||||
HOTT_rf_init();
|
||||
#ifdef HOTT_FW_TELEMETRY
|
||||
HoTT_SerialRX_val=0;
|
||||
HoTT_SerialRX=false;
|
||||
HOTT_sensor_cur=3;
|
||||
HOTT_sensor_pages=0;
|
||||
HOTT_sensor_valid=false;
|
||||
HOTT_sensor_seq=0;
|
||||
for(uint8_t i=0; i<HOTT_SENSOR_TYPE;i++)
|
||||
HOTT_sensor_ok[i]=false; // no sensors detected
|
||||
packet_count=0;
|
||||
state=HOTT_SENSOR_SEARCH_PERIOD;
|
||||
#endif
|
||||
phase = HOTT_START;
|
||||
}
|
||||
|
||||
#endif
|
||||
111
Multiprotocol/Height_a7105.ino
Normal file
111
Multiprotocol/Height_a7105.ino
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with FZ-410 TX
|
||||
|
||||
#if defined(HEIGHT_A7105_INO)
|
||||
|
||||
#include "iface_a7105.h"
|
||||
|
||||
//#define HEIGHT_FORCEID
|
||||
|
||||
#define HEIGHT_BIND_COUNT 220 // 5 sec
|
||||
#define HEIGHT_BIND_CH 0x18 // TX, RX for bind end is 0x17
|
||||
|
||||
static void __attribute__((unused)) HEIGHT_build_packet()
|
||||
{
|
||||
packet[0] = 0xA5;
|
||||
packet[1] = rx_tx_addr[2];
|
||||
packet[2] = rx_tx_addr[3];
|
||||
packet[3] = convert_channel_8b(AILERON); //00..80..FF
|
||||
packet[4] = convert_channel_8b(ELEVATOR); //00..80..FF
|
||||
packet[5] = convert_channel_8b(THROTTLE); //00..FF
|
||||
packet[6] = convert_channel_8b(RUDDER); //00..80..FF
|
||||
packet[7] = convert_channel_8b(CH5); //00..80..FF
|
||||
if(sub_protocol == HEIGHT_8CH)
|
||||
{
|
||||
packet[8] = convert_channel_8b(CH6); //00..80..FF
|
||||
packet[9] = convert_channel_8b(CH7); //00..80..FF
|
||||
packet[10] = convert_channel_8b(CH8); //00..80..FF
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t HEIGHT_callback()
|
||||
{
|
||||
#ifndef FORCE_HEIGHT_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0x1B;
|
||||
packet[1] = rx_tx_addr[2];
|
||||
packet[2] = rx_tx_addr[3];
|
||||
A7105_WriteData(3, HEIGHT_BIND_CH);
|
||||
if (bind_counter--==0)
|
||||
BIND_DONE;
|
||||
return 22700;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(phase>19)
|
||||
{
|
||||
phase=0;
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(20*1500);
|
||||
#endif
|
||||
HEIGHT_build_packet();
|
||||
A7105_WriteData(sub_protocol?11:8, hopping_frequency[0]);
|
||||
A7105_SetPower();
|
||||
}
|
||||
else
|
||||
{
|
||||
A7105_WriteReg(A7105_0F_PLL_I, hopping_frequency[(phase&0x02)>>1]);
|
||||
A7105_Strobe(A7105_TX);
|
||||
}
|
||||
phase++;
|
||||
}
|
||||
return 1500;
|
||||
}
|
||||
|
||||
void HEIGHT_init()
|
||||
{
|
||||
A7105_Init();
|
||||
|
||||
hopping_frequency[0]=((random(0xfefefefe) & 0x0F)+2)<<2;
|
||||
hopping_frequency[1]=hopping_frequency[0]+0x50;
|
||||
|
||||
#ifdef HEIGHT_FORCEID
|
||||
rx_tx_addr[2]=0x35;
|
||||
rx_tx_addr[3]=0xD0;
|
||||
hopping_frequency[0]=0x18;
|
||||
hopping_frequency[1]=0x68;
|
||||
#endif
|
||||
|
||||
phase=255;
|
||||
bind_counter = HEIGHT_BIND_COUNT;
|
||||
}
|
||||
#endif
|
||||
// Normal packet is 8 bytes: 0xA5 0xAF 0x59 0x84 0x7A 0x00 0x80 0xFF
|
||||
// Protocol is using AETR channel order, 1 byte per channel 00..80..FF including trim. Channels are in packet [3,4,5,6].
|
||||
// packet[0,1,2,7] values are constant in normal mode.
|
||||
// packet[0]=0xA5 -> normal mode
|
||||
// packet[1,2] ->ID
|
||||
// packet[7]=0xFF -> ???
|
||||
// Channel values are updated every 30ms which is quite slow, slower than PPM...
|
||||
// Packets are sent every 1500µs on 2 different channels. 2 times on first channel, 2 times on second channel and restart. The channels are changing between the files 0x08, 0x58 and 0x18, 0x68.
|
||||
//
|
||||
// Bind is sending 3 bytes on channel 0x18: 0x1B 0x35 0xD0 every 22.7ms
|
||||
// packet[0]=0x1B -> bind mode
|
||||
// packet[1,2] ->ID
|
||||
// It listens for the model on channel 0x17 and recieves 0x1B 0x35 0xD0 when the plane accepts bind.
|
||||
@@ -12,55 +12,19 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/hisky_nrf24l01.c dated 2015-03-27
|
||||
|
||||
#if defined(HISKY_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define BIND_COUNT 1000
|
||||
#define TXID_SIZE 5
|
||||
#define FREQUENCE_NUM 20
|
||||
#define HISKY_BIND_COUNT 1000
|
||||
#define HISKY_TXID_SIZE 5
|
||||
#define HISKY_FREQUENCE_NUM 20
|
||||
//
|
||||
uint8_t bind_buf_arry[4][10];
|
||||
|
||||
// HiSky protocol uses TX id as an address for nRF24L01, and uses frequency hopping sequence
|
||||
// which does not depend on this id and is passed explicitly in binding sequence. So we are free
|
||||
// to generate this sequence as we wish. It should be in the range [02..77]
|
||||
void calc_fh_channels(uint32_t seed)
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
uint32_t rnd = seed;
|
||||
|
||||
while (idx < FREQUENCE_NUM)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t count_2_26 = 0, count_27_50 = 0, count_51_74 = 0;
|
||||
|
||||
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
// Use least-significant byte. 73 is prime, so channels 76..77 are unused
|
||||
uint8_t next_ch = ((rnd >> 8) % 73) + 2;
|
||||
// Keep the distance 2 between the channels - either odd or even
|
||||
if (((next_ch ^ (uint8_t)seed) & 0x01 )== 0)
|
||||
continue;
|
||||
// Check that it's not duplicated and spread uniformly
|
||||
for (i = 0; i < idx; i++) {
|
||||
if(hopping_frequency[i] == next_ch)
|
||||
break;
|
||||
if(hopping_frequency[i] <= 26)
|
||||
count_2_26++;
|
||||
else if (hopping_frequency[i] <= 50)
|
||||
count_27_50++;
|
||||
else
|
||||
count_51_74++;
|
||||
}
|
||||
if (i != idx)
|
||||
continue;
|
||||
if ( (next_ch <= 26 && count_2_26 < 8) || (next_ch >= 27 && next_ch <= 50 && count_27_50 < 8) || (next_ch >= 51 && count_51_74 < 8) )
|
||||
hopping_frequency[idx++] = next_ch;//find hopping frequency
|
||||
}
|
||||
}
|
||||
|
||||
void build_binding_packet(void)
|
||||
static void __attribute__((unused)) HISKY_build_binding_packet(void)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t sum=0;
|
||||
@@ -94,38 +58,30 @@ void build_binding_packet(void)
|
||||
}
|
||||
}
|
||||
|
||||
void hisky_init()
|
||||
static void __attribute__((unused)) HISKY_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowledgement
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable p0 rx
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address (byte -2)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 81); // binding packet must be set in channel 81
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 10); // payload size = 10
|
||||
if(sub_protocol==HK310)
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps
|
||||
else
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_SetPower(); // Set power
|
||||
NRF24L01_SetTxRxMode(TX_EN); // TX mode, 2-bytes CRC, radio on
|
||||
}
|
||||
|
||||
// HiSky channel sequence: AILE ELEV THRO RUDD GEAR PITCH, channel data value is from 0 to 1000
|
||||
// Channel 7 - Gyro mode, 0 - 6 axis, 3 - 3 axis
|
||||
void build_ch_data()
|
||||
static void __attribute__((unused)) HISKY_build_ch_data()
|
||||
{
|
||||
uint16_t temp;
|
||||
uint8_t i,j;
|
||||
uint8_t ch[]={AILERON, ELEVATOR, THROTTLE, RUDDER, AUX1, AUX2, AUX3, AUX4};
|
||||
for (i = 0; i< 8; i++) {
|
||||
j=ch[i];
|
||||
temp=map(limit_channel_100(j),PPM_MIN_100,PPM_MAX_100,0,1000);
|
||||
if (j == THROTTLE) // It is clear that hisky's throttle stick is made reversely, so I adjust it here on purpose
|
||||
j=CH_AETR[i];
|
||||
temp=convert_channel_16b_limit(j,0,1000);
|
||||
if (j == CH3) // It is clear that hisky's throttle stick is made reversely, so I adjust it here on purpose
|
||||
temp = 1000 - temp;
|
||||
if (j == AUX3)
|
||||
if (j == CH7)
|
||||
temp = temp < 400 ? 0 : 3; // Gyro mode, 0 - 6 axis, 3 - 3 axis
|
||||
packet[i] = (uint8_t)(temp&0xFF);
|
||||
packet[i<4?8:9]>>=2;
|
||||
@@ -133,7 +89,7 @@ void build_ch_data()
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t hisky_cb()
|
||||
uint16_t HISKY_callback()
|
||||
{
|
||||
phase++;
|
||||
if(sub_protocol==HK310)
|
||||
@@ -143,15 +99,36 @@ uint16_t hisky_cb()
|
||||
NRF24L01_SetPower();
|
||||
phase=2;
|
||||
break;
|
||||
case 3:
|
||||
if (! bind_counter)
|
||||
NRF24L01_WritePayload(packet,10); // 2 packets per 5ms
|
||||
break;
|
||||
case 4:
|
||||
phase=6;
|
||||
break;
|
||||
case 7: // build packet and send failsafe every 100ms
|
||||
convert_channel_HK310(hopping_frequency_no!=0?RUDDER:AUX2,&packet[0],&packet[1]);
|
||||
convert_channel_HK310(hopping_frequency_no!=0?THROTTLE:AUX3,&packet[2],&packet[3]);
|
||||
convert_channel_HK310(hopping_frequency_no!=0?AUX1:AUX4,&packet[4],&packet[5]);
|
||||
packet[7]=hopping_frequency_no!=0?0x55:0xAA;
|
||||
packet[8]=hopping_frequency_no!=0?0x67:0x5A;
|
||||
case 7: // build packet
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(5000);
|
||||
#endif
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
if(IS_FAILSAFE_VALUES_on && hopping_frequency_no==0)
|
||||
{ // send failsafe every 100ms
|
||||
convert_failsafe_HK310(RUDDER, &packet[0],&packet[1]);
|
||||
convert_failsafe_HK310(THROTTLE,&packet[2],&packet[3]);
|
||||
convert_failsafe_HK310(CH5, &packet[4],&packet[5]);
|
||||
packet[7]=0xAA;
|
||||
packet[8]=0x5A;
|
||||
FAILSAFE_VALUES_off;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
convert_channel_HK310(RUDDER, &packet[0],&packet[1]);
|
||||
convert_channel_HK310(THROTTLE,&packet[2],&packet[3]);
|
||||
convert_channel_HK310(CH5, &packet[4],&packet[5]);
|
||||
packet[7]=0x55;
|
||||
packet[8]=0x67;
|
||||
}
|
||||
phase=8;
|
||||
break;
|
||||
}
|
||||
@@ -194,12 +171,15 @@ uint16_t hisky_cb()
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
hopping_frequency_no++;
|
||||
if (hopping_frequency_no >= FREQUENCE_NUM)
|
||||
if (hopping_frequency_no >= HISKY_FREQUENCE_NUM)
|
||||
hopping_frequency_no = 0;
|
||||
break;
|
||||
case 7:
|
||||
//Build normal packet
|
||||
build_ch_data();
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(9000);
|
||||
#endif
|
||||
HISKY_build_ch_data();
|
||||
break;
|
||||
case 8:
|
||||
break;
|
||||
@@ -212,31 +192,37 @@ uint16_t hisky_cb()
|
||||
return 1000; // send 1 binding packet and 1 data packet per 9ms
|
||||
}
|
||||
|
||||
// Generate internal id from TX id and manufacturer id (STM32 unique id)
|
||||
void initialize_tx_id()
|
||||
static void __attribute__((unused)) HISKY_initialize_tx_id()
|
||||
{
|
||||
//Generate frequency hopping table
|
||||
if(sub_protocol==HK310)
|
||||
for(uint8_t i=0;i<FREQUENCE_NUM;i++)
|
||||
hopping_frequency[i]=i; // Sequential order hop channels...
|
||||
{
|
||||
// for HiSky surface protocol, the transmitter always generates hop channels in sequential order.
|
||||
// The transmitter only generates the first hop channel between 0 and 49. So the channel range is from 0 to 69.
|
||||
hopping_frequency_no=rx_tx_addr[0]%50;
|
||||
for(uint8_t i=0;i<HISKY_FREQUENCE_NUM;i++)
|
||||
hopping_frequency[i]=hopping_frequency_no++; // Sequential order hop channels...
|
||||
}
|
||||
else
|
||||
calc_fh_channels(MProtocol_id);
|
||||
calc_fh_channels(HISKY_FREQUENCE_NUM);
|
||||
// HiSky air protocol uses TX id as an address for nRF24L01, and uses frequency hopping sequence
|
||||
// which does not depend on this id and is passed explicitly in binding sequence. So we are free
|
||||
// to generate this sequence as we wish. It should be in the range [02..77]
|
||||
}
|
||||
|
||||
uint16_t initHiSky()
|
||||
void HISKY_init()
|
||||
{
|
||||
initialize_tx_id();
|
||||
build_binding_packet();
|
||||
hisky_init();
|
||||
HISKY_initialize_tx_id();
|
||||
HISKY_build_binding_packet();
|
||||
HISKY_RF_init();
|
||||
phase = 0;
|
||||
hopping_frequency_no = 0;
|
||||
binding_idx = 0;
|
||||
|
||||
if(IS_AUTOBIND_FLAG_on)
|
||||
bind_counter = BIND_COUNT;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
bind_counter = HISKY_BIND_COUNT;
|
||||
else
|
||||
bind_counter = 0;
|
||||
return 1000;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
481
Multiprotocol/Hitec_cc2500.ino
Normal file
481
Multiprotocol/Hitec_cc2500.ino
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(HITEC_CC2500_INO)
|
||||
|
||||
#include "iface_cc2500.h"
|
||||
|
||||
//#define HITEC_FORCE_ID //Use the ID and hopping table from the original dump
|
||||
|
||||
#define HITEC_COARSE 0
|
||||
|
||||
#define HITEC_PACKET_LEN 13
|
||||
#define HITEC_TX_ID_LEN 2
|
||||
#define HITEC_BIND_COUNT 444 // 10sec
|
||||
#define HITEC_NUM_FREQUENCE 21
|
||||
#define HITEC_BIND_NUM_FREQUENCE 14
|
||||
|
||||
enum {
|
||||
HITEC_START = 0x00,
|
||||
HITEC_CALIB = 0x01,
|
||||
HITEC_PREP = 0x02,
|
||||
HITEC_DATA1 = 0x03,
|
||||
HITEC_DATA2 = 0x04,
|
||||
HITEC_DATA3 = 0x05,
|
||||
HITEC_DATA4 = 0x06,
|
||||
HITEC_RX1 = 0x07,
|
||||
HITEC_RX2 = 0x08,
|
||||
};
|
||||
|
||||
const PROGMEM uint8_t HITEC_init_values[] = {
|
||||
/* 00 */ 0x2F, 0x2E, 0x2F, 0x07, 0xD3, 0x91, 0xFF, 0x04,
|
||||
/* 08 */ 0x45, 0x00, 0x00, 0x12, 0x00, 0x5C, 0x85, 0xE8 + HITEC_COARSE,
|
||||
/* 10 */ 0x3D, 0x3B, 0x73, 0x73, 0x7A, 0x01, 0x07, 0x30,
|
||||
/* 18 */ 0x08, 0x1D, 0x1C, 0xC7, 0x40, 0xB0, 0x87, 0x6B,
|
||||
/* 20 */ 0xF8, 0xB6, 0x10, 0xEA, 0x0A, 0x00, 0x11
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) HITEC_CC2500_init()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
|
||||
for (uint8_t i = 0; i < 39; ++i)
|
||||
CC2500_WriteReg(i, pgm_read_byte_near(&HITEC_init_values[i]));
|
||||
|
||||
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
|
||||
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
CC2500_SetPower();
|
||||
}
|
||||
|
||||
// Generate RF channels
|
||||
static void __attribute__((unused)) HITEC_RF_channels()
|
||||
{
|
||||
//Normal hopping
|
||||
uint8_t idx = 0;
|
||||
uint32_t rnd = MProtocol_id;
|
||||
|
||||
while (idx < HITEC_NUM_FREQUENCE)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t count_0_47 = 0, count_48_93 = 0, count_94_140 = 0;
|
||||
|
||||
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
// Use least-significant byte and make sure it's pair.
|
||||
uint8_t next_ch = ((rnd >> 8) % 141) & 0xFE;
|
||||
// Check that it's not duplicated and spread uniformly
|
||||
for (i = 0; i < idx; i++) {
|
||||
if(hopping_frequency[i] == next_ch)
|
||||
break;
|
||||
if(hopping_frequency[i] <= 47)
|
||||
count_0_47++;
|
||||
else if (hopping_frequency[i] <= 93)
|
||||
count_48_93++;
|
||||
else
|
||||
count_94_140++;
|
||||
}
|
||||
if (i != idx)
|
||||
continue;
|
||||
if ( (next_ch <= 47 && count_0_47 < 8) || (next_ch >= 48 && next_ch <= 93 && count_48_93 < 8) || (next_ch >= 94 && count_94_140 < 8) )
|
||||
hopping_frequency[idx++] = next_ch;//find hopping frequency
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HITEC_tune_chan()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency_no*10);
|
||||
else
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[hopping_frequency_no]);
|
||||
CC2500_Strobe(CC2500_SFTX);
|
||||
CC2500_Strobe(CC2500_SCAL);
|
||||
CC2500_Strobe(CC2500_STX);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HITEC_change_chan_fast()
|
||||
{
|
||||
CC2500_Strobe(CC2500_SIDLE);
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency_no*10);
|
||||
else
|
||||
CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[hopping_frequency_no]);
|
||||
CC2500_WriteReg(CC2500_25_FSCAL1, calData[hopping_frequency_no]);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HITEC_build_packet()
|
||||
{
|
||||
static boolean F5_frame=false;
|
||||
static uint8_t F5_counter=0;
|
||||
uint8_t offset;
|
||||
|
||||
packet[1] = rx_tx_addr[1];
|
||||
packet[2] = rx_tx_addr[2];
|
||||
packet[3] = rx_tx_addr[3];
|
||||
packet[22] = 0xEE; // unknown always 0xEE
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0x16; // 22 bytes to follow
|
||||
memset(packet+5,0x00,14);
|
||||
switch(bind_phase)
|
||||
{
|
||||
case 0x72: // first part of the hopping table
|
||||
for(uint8_t i=0;i<14;i++)
|
||||
packet[5+i]=hopping_frequency[i]>>1;
|
||||
break;
|
||||
case 0x73: // second part of the hopping table
|
||||
for(uint8_t i=0;i<7;i++)
|
||||
packet[5+i]=hopping_frequency[i+14]>>1;
|
||||
break;
|
||||
case 0x74:
|
||||
packet[7]=0x55; // unknown but bind does not complete if not there
|
||||
packet[8]=0x55; // unknown but bind does not complete if not there
|
||||
break;
|
||||
case 0x7B:
|
||||
packet[5]=hopping_frequency[13]>>1; // if not there the Optima link is jerky...
|
||||
packet[14]=0x2A;
|
||||
packet[15]=0x46; // unknown but if 0x45 then 17=0x46, if 0x46 then 17=0x46 or 0x47, if 0x47 then 0x45 or 0x46
|
||||
packet[16]=0x2A;
|
||||
packet[17]=0x47;
|
||||
packet[18]=0x2A;
|
||||
break;
|
||||
}
|
||||
if(sub_protocol==MINIMA)
|
||||
packet[4] = bind_phase+0x10;
|
||||
else
|
||||
packet[4] = bind_phase; // Optima: increments based on RX answer
|
||||
packet[19] = 0x08; // packet sequence
|
||||
offset=20; // packet[20] and [21]
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[0] = 0x1A; // 26 bytes to follow
|
||||
for(uint8_t i=0;i<9;i++)
|
||||
{
|
||||
uint16_t ch = convert_channel_16b_nolimit(i,0x1B87,0x3905,false);
|
||||
packet[4+2*i] = ch >> 8;
|
||||
packet[5+2*i] = ch & 0xFF;
|
||||
}
|
||||
packet[23] = 0x80; // packet sequence
|
||||
offset=24; // packet[24] and [25]
|
||||
packet[26] = 0x00; // unknown always 0 and the RX doesn't seem to care about the value?
|
||||
}
|
||||
|
||||
if(F5_frame)
|
||||
{// No idea what it is but Minima RXs are expecting these frames to work to work
|
||||
packet[offset] = 0xF5;
|
||||
packet[offset+1] = 0xDF;
|
||||
if((F5_counter%9)==0)
|
||||
packet[offset+1] -= 0x04; // every 8 packets send 0xDB
|
||||
F5_counter++;
|
||||
F5_counter%=59; // every 6 0xDB packets wait only 4 to resend instead of 8
|
||||
F5_frame=false; // alternate
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
packet[offset+1]++; // when binding the values are 0xE0 and 0xDC
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[offset] = 0x00;
|
||||
packet[offset+1] = 0x00;
|
||||
F5_frame=true; // alternate
|
||||
}
|
||||
/* debug("P:");
|
||||
for(uint8_t i=0;i<packet[0]+1;i++)
|
||||
debug("%02X,",packet[i]);
|
||||
debugln("");
|
||||
*/
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HITEC_send_packet()
|
||||
{
|
||||
CC2500_WriteData(packet, packet[0]+1);
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[19] >>= 1; // packet sequence
|
||||
if( (packet[4] & 0xFE) ==0x82 )
|
||||
{ // Minima
|
||||
packet[4] ^= 1; // alternate 0x82 and 0x83
|
||||
if( packet[4] & 0x01 )
|
||||
for(uint8_t i=0;i<7;i++) // 0x83
|
||||
packet[5+i]=hopping_frequency[i+14]>>1;
|
||||
else
|
||||
for(uint8_t i=0;i<14;i++) // 0x82
|
||||
packet[5+i]=hopping_frequency[i]>>1;
|
||||
}
|
||||
}
|
||||
else
|
||||
packet[23] >>= 1; // packet sequence
|
||||
}
|
||||
|
||||
uint16_t HITEC_callback()
|
||||
{
|
||||
switch(phase)
|
||||
{
|
||||
case HITEC_START:
|
||||
HITEC_CC2500_init();
|
||||
bind_phase=0x72;
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
bind_counter = HITEC_BIND_COUNT;
|
||||
rf_ch_num=HITEC_BIND_NUM_FREQUENCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bind_counter=0;
|
||||
rf_ch_num=HITEC_NUM_FREQUENCE;
|
||||
//Set TXID
|
||||
CC2500_WriteReg(CC2500_05_SYNC0,rx_tx_addr[2]);
|
||||
CC2500_WriteReg(CC2500_04_SYNC1,rx_tx_addr[3]);
|
||||
}
|
||||
hopping_frequency_no=0;
|
||||
HITEC_tune_chan();
|
||||
phase = HITEC_CALIB;
|
||||
return 2000;
|
||||
case HITEC_CALIB:
|
||||
calData[hopping_frequency_no]=CC2500_ReadReg(CC2500_25_FSCAL1);
|
||||
hopping_frequency_no++;
|
||||
if (hopping_frequency_no < rf_ch_num)
|
||||
HITEC_tune_chan();
|
||||
else
|
||||
{
|
||||
hopping_frequency_no = 0;
|
||||
phase = HITEC_PREP;
|
||||
}
|
||||
return 2000;
|
||||
|
||||
/* Work cycle: 22.5ms */
|
||||
#define HITEC_PACKET_PERIOD 22500
|
||||
#define HITEC_PREP_TIMING 462
|
||||
#define HITEC_DATA_TIMING 2736
|
||||
#define HITEC_RX1_TIMING 4636
|
||||
case HITEC_PREP:
|
||||
if ( prev_option == option )
|
||||
{ // No user frequency change
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(HITEC_PACKET_PERIOD);
|
||||
#endif
|
||||
HITEC_change_chan_fast();
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no>=rf_ch_num)
|
||||
hopping_frequency_no=0;
|
||||
CC2500_SetPower();
|
||||
CC2500_SetTxRxMode(TX_EN);
|
||||
HITEC_build_packet();
|
||||
phase++;
|
||||
}
|
||||
else
|
||||
phase = HITEC_START; // Restart the tune process if option is changed to get good tuned values
|
||||
return HITEC_PREP_TIMING;
|
||||
case HITEC_DATA1:
|
||||
case HITEC_DATA2:
|
||||
case HITEC_DATA3:
|
||||
case HITEC_DATA4:
|
||||
HITEC_send_packet();
|
||||
phase++;
|
||||
return HITEC_DATA_TIMING;
|
||||
case HITEC_RX1:
|
||||
CC2500_SetTxRxMode(RX_EN);
|
||||
CC2500_Strobe(CC2500_SRX); // Turn RX ON
|
||||
phase++;
|
||||
return HITEC_RX1_TIMING;
|
||||
case HITEC_RX2:
|
||||
uint8_t len=CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
|
||||
if(len && len<TELEMETRY_BUFFER_SIZE)
|
||||
{ // Something has been received
|
||||
CC2500_ReadData(packet_in, len);
|
||||
if( (packet_in[len-1] & 0x80) && packet_in[0]==len-3 && packet_in[1]==rx_tx_addr[1] && packet_in[2]==rx_tx_addr[2] && packet_in[3]==rx_tx_addr[3])
|
||||
{ //valid crc && length ok && tx_id ok
|
||||
debug("RX:l=%d",len);
|
||||
for(uint8_t i=0;i<len;i++)
|
||||
debug(",%02X",packet_in[i]);
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
if(len==13) // Bind packets have a length of 13
|
||||
{ // bind packet: 0A,00,E5,F2,7X,05,06,07,08,09,00
|
||||
debug(",bind");
|
||||
boolean check=true;
|
||||
for(uint8_t i=5;i<10;i++)
|
||||
if(packet_in[i]!=i) check=false;
|
||||
if((packet_in[4]&0xF0)==0x70 && check)
|
||||
{
|
||||
bind_phase=packet_in[4]+1;
|
||||
if(bind_phase==0x7B)
|
||||
bind_counter=164; // in dumps the RX stops to reply at 0x7B so wait a little and exit
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if( len==15 && packet_in[4]==0 && packet_in[12]==0 )
|
||||
{ // Valid telemetry packets
|
||||
// no station:
|
||||
// 0C,1C,A1,2B,00,00,00,00,00,00,00,8D,00,64,8E -> 00 8D=>RX battery voltage 0x008D/28=5.03V
|
||||
// with HTS-SS:
|
||||
// 0C,1C,A1,2B,00,11,AF,00,2D,00,8D,11,00,4D,96 -> 00 8D=>RX battery voltage 0x008D/28=5.03V
|
||||
// 0C,1C,A1,2B,00,12,00,00,00,00,00,12,00,52,93
|
||||
// 0C,1C,A1,2B,00,13,00,00,00,00,46,13,00,52,8B -> 46=>temperature2 0x46-0x28=30°C
|
||||
// 0C,1C,A1,2B,00,14,00,00,00,00,41,14,00,2C,93 -> 41=>temperature1 0x41-0x28=25°C
|
||||
// 0C,1C,A1,2B,00,15,00,2A,00,0E,00,15,00,44,96 -> 2A 00=>rpm1=420, 0E 00=>rpm2=140
|
||||
// 0C,1C,A1,2B,00,16,00,00,00,00,00,16,00,2C,8E
|
||||
// 0C,1C,A1,2B,00,17,00,00,00,42,44,17,00,48,8D -> 42=>temperature3 0x42-0x28=26°C,44=>temperature4 0x44-0x28=28°C
|
||||
// 0C,1C,A1,2B,00,18,00,00,00,00,00,18,00,50,92
|
||||
debug(",telem,%02x",packet_in[14]&0x7F);
|
||||
#if defined(HITEC_FW_TELEMETRY) || defined(HITEC_HUB_TELEMETRY)
|
||||
TX_RSSI = packet_in[13];
|
||||
if(TX_RSSI >=128)
|
||||
TX_RSSI -= 128;
|
||||
else
|
||||
TX_RSSI += 128;
|
||||
TX_LQI = packet_in[14]&0x7F;
|
||||
#endif
|
||||
#if defined(HITEC_FW_TELEMETRY)
|
||||
if(sub_protocol==OPT_FW)
|
||||
{
|
||||
// 8 bytes telemetry packets => see at the end of this file how to fully decode it
|
||||
packet_in[0]=TX_RSSI; // TX RSSI
|
||||
packet_in[1]=TX_LQI; // TX LQI
|
||||
uint8_t offset=packet_in[5]==0?1:0;
|
||||
for(uint8_t i=5;i < 11; i++)
|
||||
packet_in[i-3]=packet_in[i+offset]; // frame number followed by 5 bytes of data
|
||||
telemetry_link=2; // telemetry forward available
|
||||
}
|
||||
#endif
|
||||
#if defined(HITEC_HUB_TELEMETRY)
|
||||
if(sub_protocol==OPT_HUB)
|
||||
{
|
||||
switch(packet_in[5]) // telemetry frame number
|
||||
{
|
||||
case 0x00:
|
||||
v_lipo1 = (packet_in[10])<<5 | (packet_in[11])>>3; // calculation in float is volt=(packet_in[10]<<8+packet_in[11])/28
|
||||
break;
|
||||
case 0x11:
|
||||
v_lipo1 = (packet_in[9])<<5 | (packet_in[10])>>3; // calculation in float is volt=(packet_in[9]<<8+packet_in[10])/28
|
||||
break;
|
||||
case 0x18:
|
||||
v_lipo2 = (packet_in[6])<<5 | (packet_in[7])>>3; // calculation in float is volt=(packet_in[6]<<8+packet_in[7])/10
|
||||
break;
|
||||
}
|
||||
telemetry_link=1; // telemetry hub available
|
||||
}
|
||||
#endif
|
||||
}
|
||||
debugln("");
|
||||
}
|
||||
}
|
||||
CC2500_Strobe(CC2500_SFRX); // Flush the RX FIFO buffer
|
||||
phase = HITEC_PREP;
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if(!bind_counter)
|
||||
{
|
||||
BIND_DONE;
|
||||
phase=HITEC_START;
|
||||
}
|
||||
}
|
||||
return (HITEC_PACKET_PERIOD -HITEC_PREP_TIMING -4*HITEC_DATA_TIMING -HITEC_RX1_TIMING);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HITEC_init()
|
||||
{
|
||||
HITEC_RF_channels();
|
||||
#ifdef HITEC_FORCE_ID // ID and channels taken from dump
|
||||
rx_tx_addr[1]=0x00;
|
||||
rx_tx_addr[2]=0x03;
|
||||
rx_tx_addr[3]=0x6A;
|
||||
memcpy((void *)hopping_frequency,(void *)"\x00\x3A\x4A\x32\x0C\x58\x2A\x10\x26\x20\x08\x60\x68\x70\x78\x80\x88\x56\x5E\x66\x6E",HITEC_NUM_FREQUENCE);
|
||||
#endif
|
||||
phase = HITEC_START;
|
||||
}
|
||||
|
||||
/* Full telemetry
|
||||
packet[0] = TX RSSI value
|
||||
packet[1] = TX LQI value
|
||||
packet[2] = frame number
|
||||
packet[3-7] telemetry data
|
||||
|
||||
The frame number takes the following values: 0x00, 0x11, 0x12, ..., 0x1C. The frames can be present or not, they also do not have to follow each others.
|
||||
Here is a description of the telemetry data for each frame number:
|
||||
- frame 0x00
|
||||
data byte 0 -> 0x00 unknown
|
||||
data byte 1 -> 0x00 unknown
|
||||
data byte 2 -> 0x00 unknown
|
||||
data byte 3 -> RX Batt Volt_H
|
||||
data byte 4 -> RX Batt Volt_L => RX Batt=(Volt_H*256+Volt_L)/28
|
||||
- frame 0x11
|
||||
data byte 0 -> 0xAF start of frame
|
||||
data byte 1 -> 0x00 unknown
|
||||
data byte 2 -> 0x2D station type 0x2D=standard station nitro or electric, 0xAC=advanced station
|
||||
data byte 3 -> RX Batt Volt_H
|
||||
data byte 4 -> RX Batt Volt_L => RX Batt=(Volt_H*256+Volt_L)/28
|
||||
- frame 0x12
|
||||
data byte 0 -> Lat_sec_H GPS : latitude second
|
||||
data byte 1 -> Lat_sec_L signed int : 1/100 of second
|
||||
data byte 2 -> Lat_deg_min_H GPS : latitude degree.minute
|
||||
data byte 3 -> Lat_deg_min_L signed int : +=North, - = south
|
||||
data byte 4 -> Time_second GPS Time
|
||||
- frame 0x13
|
||||
data byte 0 -> GPS Longitude second
|
||||
data byte 1 -> signed int : 1/100 of second
|
||||
data byte 2 -> GPS Longitude degree.minute
|
||||
data byte 3 -> signed int : +=Est, - = west
|
||||
data byte 4 -> Temp2 Temperature2=Temp2-40°C
|
||||
- frame 0x14
|
||||
data byte 0 -> Speed_H
|
||||
data byte 1 -> Speed_L GPS Speed=Speed_H*256+Speed_L km/h
|
||||
data byte 2 -> Alti_sea_H
|
||||
data byte 3 -> Alti_sea_L GPS Altitude=Alti_sea_H*256+Alti_sea_L m
|
||||
data byte 4 -> Temp1 Temperature1=Temp1-40°C
|
||||
- frame 0x15
|
||||
data byte 0 -> FUEL
|
||||
data byte 1 -> RPM1_L
|
||||
data byte 2 -> RPM1_H RPM1=RPM1_H*256+RPM1_L
|
||||
data byte 3 -> RPM2_L
|
||||
data byte 4 -> RPM2_H RPM2=RPM2_H*256+RPM2_L
|
||||
- frame 0x16
|
||||
data byte 0 -> Date_year GPS Date
|
||||
data byte 1 -> Date_month
|
||||
data byte 2 -> Date_day
|
||||
data byte 3 -> Time_hour GPS Time
|
||||
data byte 4 -> Time_min
|
||||
- frame 0x17
|
||||
data byte 0 -> COURSEH
|
||||
data byte 1 -> COURSEL GPS heading = COURSEH*256+COURSEL in degrees
|
||||
data byte 2 -> Count GPS satellites
|
||||
data byte 3 -> Temp3 Temperature3=Temp2-40°C
|
||||
data byte 4 -> Temp4 Temperature4=Temp3-40°C
|
||||
- frame 0x18
|
||||
data byte 0 -> Volt_L Volt=(Volt_H*256+Volt_L)/10 V
|
||||
data byte 1 -> Volt_H
|
||||
data byte 2 -> AMP_L
|
||||
data byte 3 -> AMP_H Amp=(AMP1_*256+AMP_L -180)/14 in signed A
|
||||
- frame 0x19 Servo sensor
|
||||
data byte 0 -> AMP_Servo1 Amp=AMP_Servo1/10 in A
|
||||
data byte 1 -> AMP_Servo2 Amp=AMP_Servo2/10 in A
|
||||
data byte 2 -> AMP_Servo3 Amp=AMP_Servo3/10 in A
|
||||
data byte 3 -> AMP_Servo4 Amp=AMP_Servo4/10 in A
|
||||
- frame 0x1A
|
||||
data byte 2 -> ASpeed_H Air speed=ASpeed_H*256+ASpeed_L km/h
|
||||
data byte 3 -> ASpeed_L
|
||||
- frame 0x1B Variometer sensor
|
||||
data byte 0 -> Alti1H
|
||||
data byte 1 -> Alti1L Altitude unfiltered
|
||||
data byte 2 -> Alti2H
|
||||
data byte 3 -> Alti2L Altitude filtered
|
||||
- frame 0x1C Unknown
|
||||
- frame 0x22 Unknown
|
||||
*/
|
||||
#endif
|
||||
228
Multiprotocol/Hontai_nrf24l01.ino
Normal file
228
Multiprotocol/Hontai_nrf24l01.ino
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(HONTAI_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define HONTAI_BIND_COUNT 80
|
||||
#define HONTAI_PACKET_PERIOD 13500
|
||||
#define FQ777_951_PACKET_PERIOD 10000
|
||||
#define HONTAI_INITIAL_WAIT 500
|
||||
#define HONTAI_BIND_PACKET_SIZE 10
|
||||
#define HONTAI_PACKET_SIZE 12
|
||||
#define HONTAI_RF_BIND_CHANNEL 0
|
||||
|
||||
enum{
|
||||
HONTAI_FLAG_FLIP = 0x01,
|
||||
HONTAI_FLAG_PICTURE = 0x02,
|
||||
HONTAI_FLAG_VIDEO = 0x04,
|
||||
HONTAI_FLAG_HEADLESS = 0x08,
|
||||
HONTAI_FLAG_RTH = 0x10,
|
||||
HONTAI_FLAG_CALIBRATE = 0x20,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) HONTAI_send_packet()
|
||||
{
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
memcpy(packet, rx_tx_addr, 5);
|
||||
memset(&packet[5], 0, 3);
|
||||
packet_length = HONTAI_BIND_PACKET_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(packet,0,HONTAI_PACKET_SIZE);
|
||||
packet[3] = convert_channel_16b_limit(THROTTLE, 0, 127) << 1; // Throttle
|
||||
packet[4] = convert_channel_16b_limit(AILERON, 63, 0); // Aileron
|
||||
packet[5] = convert_channel_16b_limit(ELEVATOR, 0, 63); // Elevator
|
||||
packet[6] = convert_channel_16b_limit(RUDDER, 0, 63); // Rudder
|
||||
if(sub_protocol == X5C1)
|
||||
packet[7] = convert_channel_16b_limit(AILERON, 0, 63)-31; // Aileron trim
|
||||
else
|
||||
packet[7] = convert_channel_16b_limit(AILERON, 0, 32)-16; // Aileron trim
|
||||
packet[8] = convert_channel_16b_limit(RUDDER, 0, 32)-16; // Rudder trim
|
||||
if (sub_protocol == X5C1)
|
||||
packet[9] = convert_channel_16b_limit(ELEVATOR, 0, 63)-31; // Elevator trim
|
||||
else
|
||||
packet[9] = convert_channel_16b_limit(ELEVATOR, 0, 32)-16; // Elevator trim
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case HONTAI:
|
||||
packet[0] = 0x0B;
|
||||
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
|
||||
packet[4] |= GET_FLAG(CH10_SW, 0x80) // RTH
|
||||
| GET_FLAG(CH9_SW, 0x40); // Headless
|
||||
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
|
||||
| GET_FLAG(CH5_SW, 0x40); // Flip
|
||||
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
|
||||
break;
|
||||
case JJRCX1:
|
||||
packet[0] = GET_FLAG(CH6_SW, 0x02); // Arm
|
||||
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
|
||||
packet[4] |= 0x80; // unknown
|
||||
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
|
||||
| GET_FLAG(CH5_SW, 0x40); // Flip
|
||||
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
|
||||
packet[8] = 0xC0 // high rate, no rudder trim
|
||||
| GET_FLAG(CH10_SW, 0x02) // RTH
|
||||
| GET_FLAG(CH9_SW, 0x01); // Headless
|
||||
break;
|
||||
case X5C1:
|
||||
packet[0] = 0x0B;
|
||||
packet[3] |= GET_FLAG(CH7_SW, 0x01); // Picture
|
||||
packet[4] = 0x80 // unknown
|
||||
| GET_FLAG(CH6_SW, 0x40); // Lights
|
||||
packet[5] |= GET_FLAG(CH11_SW, 0x80) // Calibrate
|
||||
| GET_FLAG(CH5_SW, 0x40); // Flip
|
||||
packet[6] |= GET_FLAG(CH8_SW, 0x80); // Video
|
||||
packet[8] = 0xC0 // high rate, no rudder trim
|
||||
| GET_FLAG(CH10_SW, 0x02) // RTH
|
||||
| GET_FLAG(CH9_SW, 0x01); // Headless
|
||||
break;
|
||||
case FQ777_951:
|
||||
packet[0] = GET_FLAG(CH7_SW, 0x01) // Picture
|
||||
| GET_FLAG(CH8_SW, 0x02); // Video
|
||||
packet[3] |= GET_FLAG(CH5_SW, 0x01); // Flip
|
||||
packet[4] |= 0xC0; // High rate (mid=0xa0, low=0x60)
|
||||
packet[5] |= GET_FLAG(CH11_SW, 0x80); // Calibrate
|
||||
packet[6] |= GET_FLAG(CH9_SW, 0x40); // Headless
|
||||
break;
|
||||
}
|
||||
packet_length = HONTAI_PACKET_SIZE;
|
||||
}
|
||||
|
||||
// CRC 16 bits reflected in and out
|
||||
crc=0xFFFF;
|
||||
for(uint8_t i=0; i< packet_length-2; i++)
|
||||
crc16_update(bit_reverse(packet[i]),8);
|
||||
crc ^= 0xFFFF;
|
||||
packet[packet_length-2]=bit_reverse(crc>>8);
|
||||
packet[packet_length-1]=bit_reverse(crc);
|
||||
|
||||
// Power on, TX mode, 2byte CRC
|
||||
if(sub_protocol == JJRCX1)
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
else
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? HONTAI_RF_BIND_CHANNEL : hopping_frequency[hopping_frequency_no++]);
|
||||
hopping_frequency_no %= 3;
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
if(sub_protocol == JJRCX1)
|
||||
NRF24L01_WritePayload(packet, packet_length);
|
||||
else
|
||||
XN297_WritePayload(packet, packet_length);
|
||||
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HONTAI_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if(sub_protocol == JJRCX1)
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
|
||||
else
|
||||
XN297_SetTXAddr((const uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
|
||||
|
||||
if(sub_protocol == JJRCX1)
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xff); // JJRC uses dynamic payload length
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // match other stock settings even though AA disabled...
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
|
||||
}
|
||||
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM HONTAI_hopping_frequency_nonels[][3] = {
|
||||
{0x05, 0x19, 0x28}, // Hontai
|
||||
{0x0a, 0x1e, 0x2d}}; // JJRC X1
|
||||
|
||||
const uint8_t PROGMEM HONTAI_addr_vals[4][16] = {
|
||||
{0x24, 0x26, 0x2a, 0x2c, 0x32, 0x34, 0x36, 0x4a, 0x4c, 0x4e, 0x54, 0x56, 0x5a, 0x64, 0x66, 0x6a},
|
||||
{0x92, 0x94, 0x96, 0x9a, 0xa4, 0xa6, 0xac, 0xb2, 0xb4, 0xb6, 0xca, 0xcc, 0xd2, 0xd4, 0xd6, 0xda},
|
||||
{0x93, 0x95, 0x99, 0x9b, 0xa5, 0xa9, 0xab, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xcd, 0xd3, 0xd5, 0xd9},
|
||||
{0x25, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x49, 0x4b, 0x4d, 0x59, 0x5b, 0x65, 0x69, 0x6b, 0x6d, 0x6e}};
|
||||
|
||||
static void __attribute__((unused)) HONTAI_init2()
|
||||
{
|
||||
uint8_t data_tx_addr[5];
|
||||
|
||||
//TX address
|
||||
data_tx_addr[0] = pgm_read_byte_near( &HONTAI_addr_vals[0][ rx_tx_addr[3] & 0x0f]);
|
||||
data_tx_addr[1] = pgm_read_byte_near( &HONTAI_addr_vals[1][(rx_tx_addr[3] >> 4) & 0x0f]);
|
||||
data_tx_addr[2] = pgm_read_byte_near( &HONTAI_addr_vals[2][ rx_tx_addr[4] & 0x0f]);
|
||||
data_tx_addr[3] = pgm_read_byte_near( &HONTAI_addr_vals[3][(rx_tx_addr[4] >> 4) & 0x0f]);
|
||||
data_tx_addr[4] = 0x24;
|
||||
if(sub_protocol == JJRCX1)
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, data_tx_addr, sizeof(data_tx_addr));
|
||||
else
|
||||
XN297_SetTXAddr(data_tx_addr, sizeof(data_tx_addr));
|
||||
|
||||
//Hopping frequency table
|
||||
for(uint8_t i=0;i<3;i++)
|
||||
hopping_frequency[i]=pgm_read_byte_near( &HONTAI_hopping_frequency_nonels[sub_protocol == JJRCX1?1:0][i] );
|
||||
hopping_frequency_no=0;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) HONTAI_initialize_txid()
|
||||
{
|
||||
rx_tx_addr[4] = rx_tx_addr[2];
|
||||
if(sub_protocol == HONTAI || sub_protocol == FQ777_951)
|
||||
{
|
||||
rx_tx_addr[0] = 0x4c; // first three bytes some kind of model id? - set same as stock tx
|
||||
rx_tx_addr[1] = 0x4b;
|
||||
rx_tx_addr[2] = 0x3a;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx_tx_addr[0] = 0x4b; // JJRC X1
|
||||
rx_tx_addr[1] = 0x59;
|
||||
rx_tx_addr[2] = 0x3a;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t HONTAI_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
HONTAI_init2();
|
||||
BIND_DONE;
|
||||
}
|
||||
}
|
||||
HONTAI_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void HONTAI_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = HONTAI_BIND_COUNT;
|
||||
HONTAI_initialize_txid();
|
||||
HONTAI_RF_init();
|
||||
packet_period = sub_protocol == FQ777_951 ? FQ777_951_PACKET_PERIOD : HONTAI_PACKET_PERIOD;
|
||||
}
|
||||
#endif
|
||||
@@ -12,19 +12,63 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with Hubsan H102D, H107/L/C/D and H107P/C+/D+
|
||||
// Last sync with hexfet new_protocols/hubsan_a7105.c dated 2015-12-11
|
||||
|
||||
#if defined(HUBSAN_A7105_INO)
|
||||
|
||||
#include "iface_a7105.h"
|
||||
|
||||
enum{
|
||||
// flags going to packet[9] (H107)
|
||||
HUBSAN_FLAG_VIDEO= 0x01, // record video
|
||||
HUBSAN_FLAG_FLIP = 0x08,
|
||||
HUBSAN_FLAG_LIGHT = 0x04
|
||||
HUBSAN_FLAG_FLIP = 0x08, // enable flips
|
||||
HUBSAN_FLAG_LED = 0x04 // enable LEDs
|
||||
};
|
||||
|
||||
uint32_t sessionid;
|
||||
const uint32_t txid = 0xdb042679;
|
||||
enum{
|
||||
// flags going to packet[9] (H107 Plus series)
|
||||
HUBSAN_FLAG_HEADLESS = 0x08, // headless mode
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[9] (H301)
|
||||
FLAG_H301_VIDEO = 0x01,
|
||||
FLAG_H301_STAB = 0x02,
|
||||
FLAG_H301_LED = 0x10,
|
||||
FLAG_H301_RTH = 0x40,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[13] (H107 Plus series)
|
||||
HUBSAN_FLAG_SNAPSHOT = 0x01,
|
||||
HUBSAN_FLAG_FLIP_PLUS = 0x80,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[9] (H501S)
|
||||
FLAG_H501_VIDEO = 0x01,
|
||||
FLAG_H501_LED = 0x04,
|
||||
FLAG_H122D_FLIP = 0x08, //H122D
|
||||
FLAG_H501_RTH = 0x20,
|
||||
FLAG_H501_HEADLESS1 = 0x40,
|
||||
FLAG_H501_GPS_HOLD = 0x80,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[11] (H122D & H123D)
|
||||
FLAG_H123D_FMODES = 0x03, //H123D 3 FMODES: Sport mode 1, Sport mode 2, Acro
|
||||
FLAG_H122D_OSD = 0x20, //H122D OSD
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[13] (H501S)
|
||||
FLAG_H501_SNAPSHOT = 0x01,
|
||||
FLAG_H501_HEADLESS2 = 0x02,
|
||||
FLAG_H501_ALT_HOLD = 0x08,
|
||||
};
|
||||
|
||||
uint32_t hubsan_id_data;
|
||||
|
||||
enum {
|
||||
BIND_1,
|
||||
@@ -41,9 +85,9 @@ enum {
|
||||
DATA_4,
|
||||
DATA_5,
|
||||
};
|
||||
#define WAIT_WRITE 0x80
|
||||
#define HUBSAN_WAIT_WRITE 0x80
|
||||
|
||||
void update_crc()
|
||||
static void __attribute__((unused)) hubsan_update_crc()
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
for(uint8_t i = 0; i < 15; i++)
|
||||
@@ -51,113 +95,250 @@ void update_crc()
|
||||
packet[15] = (256 - (sum % 256)) & 0xFF;
|
||||
}
|
||||
|
||||
void hubsan_build_bind_packet(uint8_t state)
|
||||
static void __attribute__((unused)) hubsan_build_bind_packet(uint8_t bind_state)
|
||||
{
|
||||
packet[0] = state;
|
||||
static uint8_t handshake_counter;
|
||||
if(phase < BIND_7)
|
||||
handshake_counter = 0;
|
||||
memset(packet, 0, 16);
|
||||
packet[0] = bind_state;
|
||||
packet[1] = channel;
|
||||
packet[2] = (sessionid >> 24) & 0xFF;
|
||||
packet[3] = (sessionid >> 16) & 0xFF;
|
||||
packet[4] = (sessionid >> 8) & 0xFF;
|
||||
packet[5] = (sessionid >> 0) & 0xFF;
|
||||
packet[2] = (MProtocol_id >> 24) & 0xFF;
|
||||
packet[3] = (MProtocol_id >> 16) & 0xFF;
|
||||
packet[4] = (MProtocol_id >> 8) & 0xFF;
|
||||
packet[5] = (MProtocol_id >> 0) & 0xFF;
|
||||
if(hubsan_id_data == ID_NORMAL && sub_protocol != H501)
|
||||
{
|
||||
packet[6] = 0x08;
|
||||
packet[7] = 0xe4;
|
||||
packet[8] = 0xea;
|
||||
packet[9] = 0x9e;
|
||||
packet[10] = 0x50;
|
||||
packet[11] = (txid >> 24) & 0xFF;
|
||||
packet[12] = (txid >> 16) & 0xFF;
|
||||
packet[13] = (txid >> 8) & 0xFF;
|
||||
packet[14] = (txid >> 0) & 0xFF;
|
||||
update_crc();
|
||||
//const uint32_t txid = 0xdb042679;
|
||||
packet[11] = 0xDB;
|
||||
packet[12] = 0x04;
|
||||
packet[13] = 0x26;
|
||||
packet[14] = 0x79;
|
||||
}
|
||||
else
|
||||
{ //ID_PLUS
|
||||
if(phase >= BIND_3)
|
||||
{
|
||||
packet[7] = 0x62;
|
||||
packet[8] = 0x16;
|
||||
}
|
||||
if(phase == BIND_7)
|
||||
packet[2] = handshake_counter++;
|
||||
}
|
||||
hubsan_update_crc();
|
||||
}
|
||||
|
||||
//cc : throttle observed range: 0x00 - 0xFF (smaller is down)
|
||||
//ee : rudder observed range: 0x34 - 0xcc (smaller is right)52-204-60%
|
||||
//gg : elevator observed range: 0x3e - 0xbc (smaller is up)62-188 -50%
|
||||
//ii : aileron observed range: 0x45 - 0xc3 (smaller is right)69-195-50%
|
||||
void hubsan_build_packet()
|
||||
static void __attribute__((unused)) hubsan_build_packet()
|
||||
{
|
||||
static uint8_t vtx_freq = 0;
|
||||
static uint8_t vtx_freq = 0, h501_packet = 0;
|
||||
memset(packet, 0, 16);
|
||||
if(vtx_freq != option || packet_count==100) // set vTX frequency (H107D)
|
||||
{
|
||||
vtx_freq = option;
|
||||
packet[0] = 0x40;
|
||||
packet[1] = (option>0xF2)?0x17:0x16;
|
||||
packet[2] = option+0x0D; // 5645 - 5900 MHz
|
||||
packet[0] = 0x40; // vtx data packet
|
||||
packet[1] = (vtx_freq>0xF2)?0x17:0x16;
|
||||
packet[2] = vtx_freq+0x0D; // 5645 - 5900 MHz
|
||||
packet[3] = 0x82;
|
||||
packet_count++;
|
||||
}
|
||||
else //20 00 00 00 80 00 7d 00 84 02 64 db 04 26 79 7b
|
||||
{
|
||||
packet[0] = 0x20;
|
||||
packet[2] = convert_channel_8b(THROTTLE);//throtle
|
||||
packet[0] = 0x20; // normal data packet
|
||||
packet[2] = convert_channel_8b(THROTTLE); //Throtle
|
||||
}
|
||||
packet[4] = 0xFF - convert_channel_8b(RUDDER); //Rudder is reversed
|
||||
packet[6] = 0xFF - convert_channel_8b(ELEVATOR); //Elevator is reversed
|
||||
packet[8] = convert_channel_8b(AILERON);//aileron
|
||||
if( packet_count < 100) {
|
||||
packet[9] = 0x02 | HUBSAN_FLAG_LIGHT | HUBSAN_FLAG_FLIP;
|
||||
packet[8] = convert_channel_8b(AILERON); //Aileron
|
||||
if(hubsan_id_data == ID_NORMAL && sub_protocol==H107)
|
||||
{// H107/L/C/D, H102D
|
||||
if( packet_count < 100)
|
||||
{
|
||||
packet[9] = 0x02 | HUBSAN_FLAG_LED | HUBSAN_FLAG_FLIP; // sends default value for the 100 first packets
|
||||
packet_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[9] = 0x02;
|
||||
// Channel 5
|
||||
if( Servo_data[AUX1] >= PPM_SWITCH)
|
||||
packet[9] |= HUBSAN_FLAG_FLIP;
|
||||
if(CH5_SW) packet[9] |= HUBSAN_FLAG_FLIP;
|
||||
// Channel 6
|
||||
if( Servo_data[AUX2] >= PPM_SWITCH)
|
||||
packet[9] |= HUBSAN_FLAG_LIGHT;
|
||||
if(CH6_SW) packet[9] |= HUBSAN_FLAG_LED;
|
||||
// Channel 8
|
||||
if( Servo_data[AUX4] > PPM_SWITCH)
|
||||
packet[9] |= HUBSAN_FLAG_VIDEO;
|
||||
if(CH8_SW) packet[9] |= HUBSAN_FLAG_VIDEO; // H102D
|
||||
}
|
||||
packet[10] = 0x64;
|
||||
packet[11] = (txid >> 24) & 0xFF;
|
||||
packet[12] = (txid >> 16) & 0xFF;
|
||||
packet[13] = (txid >> 8) & 0xFF;
|
||||
packet[14] = (txid >> 0) & 0xFF;
|
||||
update_crc();
|
||||
//const uint32_t txid = 0xdb042679;
|
||||
packet[11] = 0xDB;
|
||||
packet[12] = 0x04;
|
||||
packet[13] = 0x26;
|
||||
packet[14] = 0x79;
|
||||
} else if(sub_protocol==H301)
|
||||
{// H301
|
||||
if( packet_count < 100)
|
||||
{
|
||||
packet[9] = FLAG_H301_STAB; // sends default value for the 100 first packets
|
||||
packet_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[9] = GET_FLAG(CH6_SW, FLAG_H301_LED)
|
||||
| GET_FLAG(CH7_SW, FLAG_H301_STAB)
|
||||
| GET_FLAG(CH8_SW, FLAG_H301_VIDEO)
|
||||
| GET_FLAG(CH5_SW, FLAG_H301_RTH);
|
||||
}
|
||||
packet[10] = 0x18; // ?
|
||||
packet[12] = 0x5c; // ?
|
||||
packet[14] = 0xf6; // ?
|
||||
}
|
||||
else
|
||||
{ //ID_PLUS && H501
|
||||
packet[3] = sub_protocol==H501 ? 0x00:0x64;
|
||||
packet[5] = sub_protocol==H501 ? 0x00:0x64;
|
||||
packet[7] = sub_protocol==H501 ? 0x00:0x64;
|
||||
|
||||
if(sub_protocol==H501)
|
||||
{ // H501S
|
||||
packet[9] = 0x02
|
||||
| GET_FLAG(CH6_SW, FLAG_H501_LED)
|
||||
| GET_FLAG(CH8_SW, FLAG_H501_VIDEO)
|
||||
| GET_FLAG(CH12_SW, FLAG_H122D_FLIP) // H122D specific -> flip
|
||||
| GET_FLAG(CH5_SW, FLAG_H501_RTH)
|
||||
| GET_FLAG(CH10_SW, FLAG_H501_GPS_HOLD)
|
||||
| GET_FLAG(CH9_SW, FLAG_H501_HEADLESS1);
|
||||
//packet[10]= 0x1A;
|
||||
|
||||
//packet[11] content 0x00 is default
|
||||
//H123D specific -> Flight modes
|
||||
packet[11] = 0x41; // Sport mode 1
|
||||
if(Channel_data[CH13]>CHANNEL_MAX_COMMAND)
|
||||
packet[11]=0x43; // Acro
|
||||
else if(Channel_data[CH13]>CHANNEL_MIN_COMMAND)
|
||||
packet[11]=0x42; // Sport mode 2
|
||||
//H122D specific -> OSD but useless...
|
||||
//packet[11]|= 0x80
|
||||
// | GET_FLAG(CHXX_SW,FLAG_H122D_OSD);
|
||||
|
||||
packet[13] = GET_FLAG(CH9_SW, FLAG_H501_HEADLESS2)
|
||||
| GET_FLAG(CH11_SW, FLAG_H501_ALT_HOLD)
|
||||
| GET_FLAG(CH7_SW, FLAG_H501_SNAPSHOT);
|
||||
}
|
||||
else
|
||||
{ // H107P/C+/D+
|
||||
packet[9] = 0x06;
|
||||
//FLIP|LIGHT|PICTURE|VIDEO|HEADLESS
|
||||
if(CH8_SW) packet[9] |= HUBSAN_FLAG_VIDEO;
|
||||
if(CH9_SW) packet[9] |= HUBSAN_FLAG_HEADLESS;
|
||||
packet[10]= 0x19;
|
||||
packet[12]= 0x5C; // ghost channel ?
|
||||
packet[13] = 0;
|
||||
if(CH7_SW) packet[13] = HUBSAN_FLAG_SNAPSHOT;
|
||||
if(CH5_SW) packet[13] |= HUBSAN_FLAG_FLIP_PLUS;
|
||||
packet[14]= 0x49; // ghost channel ?
|
||||
}
|
||||
if(packet_count < 100)
|
||||
{ // set channels to neutral for first 100 packets
|
||||
packet[2] = 0x80; // throttle neutral is at mid stick on plus series
|
||||
packet[4] = 0x80;
|
||||
packet[6] = 0x80;
|
||||
packet[8] = 0x80;
|
||||
packet[9] = 0x06;
|
||||
packet[13]= 0x00;
|
||||
packet_count++;
|
||||
}
|
||||
if(sub_protocol==H501)
|
||||
{ // H501S
|
||||
h501_packet++;
|
||||
if(h501_packet == 10)
|
||||
{
|
||||
memset(packet, 0, 16);
|
||||
packet[0] = 0xe8;
|
||||
}
|
||||
else if(h501_packet == 20)
|
||||
{
|
||||
memset(packet, 0, 16);
|
||||
packet[0] = 0xe9;
|
||||
}
|
||||
if(h501_packet >= 20) h501_packet = 0;
|
||||
}
|
||||
}
|
||||
hubsan_update_crc();
|
||||
}
|
||||
|
||||
uint8_t hubsan_check_integrity()
|
||||
#ifdef HUBSAN_HUB_TELEMETRY
|
||||
static uint8_t __attribute__((unused)) hubsan_check_integrity()
|
||||
{
|
||||
if( (packet[0]&0xFE) != 0xE0 )
|
||||
return 0;
|
||||
uint8_t sum = 0;
|
||||
for(int i = 0; i < 15; i++)
|
||||
for(uint8_t i = 0; i < 15; i++)
|
||||
sum += packet[i];
|
||||
return packet[15] == ((256 - (sum % 256)) & 0xFF);
|
||||
return ( packet[15] == (uint8_t)(-sum) );
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t ReadHubsan()
|
||||
uint16_t HUBSAN_callback()
|
||||
{
|
||||
static uint8_t txState=0;
|
||||
#ifdef HUBSAN_HUB_TELEMETRY
|
||||
static uint8_t rfMode=0;
|
||||
#endif
|
||||
static uint8_t txState=0;
|
||||
uint16_t delay;
|
||||
uint8_t i;
|
||||
|
||||
switch(phase) {
|
||||
#ifndef FORCE_HUBSAN_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
switch(phase)
|
||||
{
|
||||
case BIND_1:
|
||||
bind_phase++;
|
||||
if(bind_phase >= 20 && sub_protocol != H501)
|
||||
{
|
||||
if(hubsan_id_data == ID_NORMAL)
|
||||
hubsan_id_data = ID_PLUS;
|
||||
else
|
||||
hubsan_id_data = ID_NORMAL;
|
||||
A7105_WriteID(hubsan_id_data);
|
||||
bind_phase = 0;
|
||||
}
|
||||
case BIND_3:
|
||||
case BIND_5:
|
||||
case BIND_7:
|
||||
hubsan_build_bind_packet(phase == BIND_7 ? 9 : (phase == BIND_5 ? 1 : phase + 1 - BIND_1));
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
A7105_WriteData(16, channel);
|
||||
phase |= WAIT_WRITE;
|
||||
phase |= HUBSAN_WAIT_WRITE;
|
||||
return 3000;
|
||||
case BIND_1 | WAIT_WRITE:
|
||||
case BIND_3 | WAIT_WRITE:
|
||||
case BIND_5 | WAIT_WRITE:
|
||||
case BIND_7 | WAIT_WRITE:
|
||||
case BIND_1 | HUBSAN_WAIT_WRITE:
|
||||
case BIND_3 | HUBSAN_WAIT_WRITE:
|
||||
case BIND_5 | HUBSAN_WAIT_WRITE:
|
||||
case BIND_7 | HUBSAN_WAIT_WRITE:
|
||||
//wait for completion
|
||||
for(i = 0; i< 20; i++) {
|
||||
for(i = 0; i< 20; i++)
|
||||
if(! (A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
break;
|
||||
}
|
||||
A7105_SetTxRxMode(RX_EN);
|
||||
A7105_Strobe(A7105_RX);
|
||||
phase &= ~WAIT_WRITE;
|
||||
phase &= ~HUBSAN_WAIT_WRITE;
|
||||
if(hubsan_id_data == ID_PLUS)
|
||||
{
|
||||
if(phase == BIND_7 && packet[2] == 9)
|
||||
{
|
||||
phase = DATA_1;
|
||||
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
|
||||
BIND_DONE;
|
||||
return 4500;
|
||||
}
|
||||
}
|
||||
phase++;
|
||||
return 4500; //7.5msec elapsed since last write
|
||||
case BIND_2:
|
||||
@@ -168,7 +349,7 @@ uint16_t ReadHubsan()
|
||||
phase = BIND_1;
|
||||
return 4500; //No signal, restart binding procedure. 12msec elapsed since last write
|
||||
}
|
||||
A7105_ReadData();
|
||||
A7105_ReadData(16);
|
||||
phase++;
|
||||
if (phase == BIND_5)
|
||||
A7105_WriteID(((uint32_t)packet[2] << 24) | ((uint32_t)packet[3] << 16) | ((uint32_t)packet[4] << 8) | packet[5]);
|
||||
@@ -179,8 +360,8 @@ uint16_t ReadHubsan()
|
||||
phase = BIND_7;
|
||||
return 15000; //22.5msec elapsed since last write
|
||||
}
|
||||
A7105_ReadData();
|
||||
if(packet[1] == 9) {
|
||||
A7105_ReadData(16);
|
||||
if(packet[1] == 9 && hubsan_id_data == ID_NORMAL) {
|
||||
phase = DATA_1;
|
||||
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
|
||||
BIND_DONE;
|
||||
@@ -195,12 +376,22 @@ uint16_t ReadHubsan()
|
||||
case DATA_4:
|
||||
case DATA_5:
|
||||
if( txState == 0) { // send packet
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(10000);
|
||||
#endif
|
||||
#ifdef HUBSAN_HUB_TELEMETRY
|
||||
rfMode = A7105_TX;
|
||||
#endif
|
||||
if( phase == DATA_1)
|
||||
A7105_SetPower(); //Keep transmit power in sync
|
||||
hubsan_build_packet();
|
||||
A7105_Strobe(A7105_STANDBY);
|
||||
A7105_WriteData(16, phase == DATA_5 ? channel + 0x23 : channel);
|
||||
uint8_t ch;
|
||||
if((phase == DATA_5 && hubsan_id_data == ID_NORMAL) && sub_protocol == H107)
|
||||
ch = channel + 0x23;
|
||||
else
|
||||
ch = channel;
|
||||
A7105_WriteData(16, ch);
|
||||
if (phase == DATA_5)
|
||||
phase = DATA_1;
|
||||
else
|
||||
@@ -208,8 +399,9 @@ uint16_t ReadHubsan()
|
||||
delay=3000;
|
||||
}
|
||||
else {
|
||||
#if defined(TELEMETRY)
|
||||
if( rfMode == A7105_TX) {// switch to rx mode 3ms after packet sent
|
||||
#ifdef HUBSAN_HUB_TELEMETRY
|
||||
if( rfMode == A7105_TX)
|
||||
{// switch to rx mode 3ms after packet sent
|
||||
for( i=0; i<10; i++)
|
||||
{
|
||||
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)) {// wait for tx completion
|
||||
@@ -220,15 +412,24 @@ uint16_t ReadHubsan()
|
||||
}
|
||||
}
|
||||
}
|
||||
if( rfMode == A7105_RX) { // check for telemetry frame
|
||||
for( i=0; i<10; i++) {
|
||||
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)) { // data received
|
||||
A7105_ReadData();
|
||||
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01)){ // data received
|
||||
v_lipo=packet[13];// hubsan lipo voltage 8bits the real value is h_lipo/10(0x2A=42-4.2V)
|
||||
if( rfMode == A7105_RX)
|
||||
{ // check for telemetry frame
|
||||
for( i=0; i<10; i++)
|
||||
{
|
||||
if( !(A7105_ReadReg(A7105_00_MODE) & 0x01))
|
||||
{ // data received
|
||||
A7105_ReadData(16);
|
||||
if( hubsan_check_integrity() )
|
||||
{
|
||||
v_lipo1=packet[13]*2;// hubsan lipo voltage 8bits the real value is h_lipo/10(0x2A=42 -> 4.2V)
|
||||
telemetry_link=1;
|
||||
}
|
||||
A7105_Strobe(A7105_RX);
|
||||
// Read TX RSSI
|
||||
int16_t temp=256-(A7105_ReadReg(A7105_1D_RSSI_THOLD)*8)/5; // value from A7105 is between 8 for maximum signal strength to 160 or less
|
||||
if(temp<0) temp=0;
|
||||
else if(temp>255) temp=255;
|
||||
TX_RSSI=temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -245,18 +446,27 @@ uint16_t ReadHubsan()
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t initHubsan() {
|
||||
void HUBSAN_init()
|
||||
{
|
||||
const uint8_t allowed_ch[] = {0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64, 0x6e, 0x78, 0x82};
|
||||
A7105_Init(INIT_HUBSAN); //hubsan_init();
|
||||
A7105_Init();
|
||||
|
||||
randomSeed((uint32_t)analogRead(A0) << 10 | analogRead(A4));
|
||||
sessionid = random(0xfefefefe) + ((uint32_t)random(0xfefefefe) << 16);
|
||||
channel = allowed_ch[random(0xfefefefe) % sizeof(allowed_ch)];
|
||||
channel = allowed_ch[MProtocol_id % sizeof(allowed_ch)];
|
||||
hubsan_id_data=ID_NORMAL;
|
||||
|
||||
if(IS_BIND_IN_PROGRESS || sub_protocol==H107)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
phase = BIND_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
phase = DATA_1;
|
||||
A7105_WriteID(MProtocol_id);
|
||||
A7105_WriteReg(A7105_1F_CODE_I, 0x0F);
|
||||
}
|
||||
packet_count=0;
|
||||
return 10000;
|
||||
bind_phase=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
239
Multiprotocol/J6Pro_cyrf6936.ino
Normal file
239
Multiprotocol/J6Pro_cyrf6936.ino
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(J6PRO_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
enum PktState {
|
||||
J6PRO_BIND,
|
||||
J6PRO_BIND_01,
|
||||
J6PRO_BIND_03_START,
|
||||
J6PRO_BIND_03_CHECK,
|
||||
J6PRO_BIND_05_1,
|
||||
J6PRO_BIND_05_2,
|
||||
J6PRO_BIND_05_3,
|
||||
J6PRO_BIND_05_4,
|
||||
J6PRO_BIND_05_5,
|
||||
J6PRO_BIND_05_6,
|
||||
J6PRO_CHANSEL,
|
||||
J6PRO_CHAN_1,
|
||||
J6PRO_CHAN_2,
|
||||
J6PRO_CHAN_3,
|
||||
J6PRO_CHAN_4,
|
||||
};
|
||||
|
||||
const uint8_t PROGMEM j6pro_bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49};
|
||||
const uint8_t j6pro_data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f};
|
||||
|
||||
static void __attribute__((unused)) j6pro_build_bind_packet()
|
||||
{
|
||||
packet[0] = 0x01; //Packet type
|
||||
packet[1] = 0x01; //FIXME: What is this? Model number maybe?
|
||||
packet[2] = 0x56; //FIXME: What is this?
|
||||
packet[3] = cyrfmfg_id[0];
|
||||
packet[4] = cyrfmfg_id[1];
|
||||
packet[5] = cyrfmfg_id[2];
|
||||
packet[6] = cyrfmfg_id[3];
|
||||
packet[7] = cyrfmfg_id[4];
|
||||
packet[8] = cyrfmfg_id[5];
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) j6pro_build_data_packet()
|
||||
{
|
||||
uint8_t i;
|
||||
uint32_t upperbits = 0;
|
||||
uint16_t value;
|
||||
packet[0] = 0xaa; //FIXME what is this?
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
value = convert_channel_10b(CH_AETR[i], false);
|
||||
packet[i+1] = value & 0xff;
|
||||
upperbits |= (value >> 8) << (i * 2);
|
||||
}
|
||||
packet[13] = upperbits & 0xff;
|
||||
packet[14] = (upperbits >> 8) & 0xff;
|
||||
packet[15] = (upperbits >> 16) & 0xff;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) j6pro_cyrf_init()
|
||||
{
|
||||
/* Initialise CYRF chip */
|
||||
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
|
||||
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3c);
|
||||
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
|
||||
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
|
||||
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
|
||||
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
|
||||
//CYRF_SetPower(0x05);
|
||||
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
|
||||
CYRF_SetPower(0x28);
|
||||
CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0e);
|
||||
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xee);
|
||||
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
|
||||
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x00);
|
||||
CYRF_ConfigDataCode(j6pro_data_code, 16);
|
||||
CYRF_WritePreamble(0x333302);
|
||||
|
||||
CYRF_GetMfgData(cyrfmfg_id);
|
||||
//Model match
|
||||
cyrfmfg_id[3]+=RX_num;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) cyrf_bindinit()
|
||||
{
|
||||
/* Use when binding */
|
||||
CYRF_SetPower(0x28); //Deviation using max power, replaced by bind power...
|
||||
//CYRF_ConfigRFChannel(0x52);
|
||||
CYRF_PROGMEM_ConfigSOPCode(j6pro_bind_sop_code);
|
||||
CYRF_ConfigCRCSeed(0x0000);
|
||||
//CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
|
||||
//CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80);
|
||||
//CYRF_ConfigRFChannel(0x52);
|
||||
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
|
||||
//CYRF_WriteRegister(CYRF_02_TX_CTRL, 0x40);
|
||||
j6pro_build_bind_packet();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) cyrf_datainit()
|
||||
{
|
||||
/* Use when already bound */
|
||||
uint8_t sop_idx = (0xff & (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[5])) % 19;
|
||||
crc = (0xff & (cyrfmfg_id[1] - cyrfmfg_id[4] + cyrfmfg_id[5])) |
|
||||
((0xff & (cyrfmfg_id[2] + cyrfmfg_id[3] - cyrfmfg_id[4] + cyrfmfg_id[5])) << 8);
|
||||
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
|
||||
CYRF_PROGMEM_ConfigSOPCode(DEVO_j6pro_sopcodes[sop_idx]);
|
||||
CYRF_ConfigCRCSeed(crc);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) j6pro_set_radio_channels()
|
||||
{
|
||||
//FIXME: Query free channels
|
||||
//lowest channel is 0x08, upper channel is 0x4d?
|
||||
CYRF_FindBestChannels(hopping_frequency, 3, 5, 8, 77);
|
||||
hopping_frequency[3] = hopping_frequency[0];
|
||||
}
|
||||
|
||||
uint16_t J6PRO_callback()
|
||||
{
|
||||
uint16_t start;
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case J6PRO_BIND:
|
||||
cyrf_bindinit();
|
||||
phase = J6PRO_BIND_01;
|
||||
//no break because we want to send the 1st bind packet now
|
||||
case J6PRO_BIND_01:
|
||||
CYRF_ConfigRFChannel(0x52);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
|
||||
CYRF_WriteDataPacketLen(packet, 0x09);
|
||||
phase = J6PRO_BIND_03_START;
|
||||
return 3000; //3msec
|
||||
case J6PRO_BIND_03_START:
|
||||
start=(uint16_t)micros();
|
||||
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 500) // Wait max 500µs
|
||||
if((CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80) == 0x00)
|
||||
break; // Packet transmission complete
|
||||
CYRF_ConfigRFChannel(0x53);
|
||||
CYRF_SetTxRxMode(RX_EN);
|
||||
//CYRF_WriteRegister(CYRF_06_RX_CFG, 0x4a);
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80);
|
||||
phase = J6PRO_BIND_03_CHECK;
|
||||
return 30000; //30msec
|
||||
case J6PRO_BIND_03_CHECK:
|
||||
{
|
||||
uint8_t rx = CYRF_ReadRegister(CYRF_07_RX_IRQ_STATUS);
|
||||
if((rx & 0x1a) == 0x1a) {
|
||||
rx = CYRF_ReadRegister(CYRF_0A_RX_LENGTH);
|
||||
if(rx == 0x0f) {
|
||||
rx = CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
if(rx == 0x0f) {
|
||||
//Expected and actual length are both 15
|
||||
CYRF_ReadDataPacketLen(packet, rx);
|
||||
if (packet[0] == 0x03 &&
|
||||
packet[3] == cyrfmfg_id[0] &&
|
||||
packet[4] == cyrfmfg_id[1] &&
|
||||
packet[5] == cyrfmfg_id[2] &&
|
||||
packet[6] == cyrfmfg_id[3] &&
|
||||
packet[7] == cyrfmfg_id[4] &&
|
||||
packet[8] == cyrfmfg_id[5])
|
||||
{
|
||||
//Send back Ack
|
||||
packet[0] = 0x05;
|
||||
CYRF_ConfigRFChannel(0x54);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
phase = J6PRO_BIND_05_1;
|
||||
return 2000; //2msec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
phase = J6PRO_BIND_01;
|
||||
return 500;
|
||||
}
|
||||
case J6PRO_BIND_05_1:
|
||||
case J6PRO_BIND_05_2:
|
||||
case J6PRO_BIND_05_3:
|
||||
case J6PRO_BIND_05_4:
|
||||
case J6PRO_BIND_05_5:
|
||||
case J6PRO_BIND_05_6:
|
||||
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
|
||||
CYRF_WriteDataPacketLen(packet, 0x0f);
|
||||
phase = phase + 1;
|
||||
return 4600; //4.6msec
|
||||
case J6PRO_CHANSEL:
|
||||
BIND_DONE;
|
||||
j6pro_set_radio_channels();
|
||||
cyrf_datainit();
|
||||
phase = J6PRO_CHAN_1;
|
||||
case J6PRO_CHAN_1:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(24550);
|
||||
#endif
|
||||
//Keep transmit power updated
|
||||
CYRF_SetPower(0x28);
|
||||
j6pro_build_data_packet();
|
||||
//return 3400;
|
||||
case J6PRO_CHAN_2:
|
||||
//return 3500;
|
||||
case J6PRO_CHAN_3:
|
||||
//return 3750
|
||||
case J6PRO_CHAN_4:
|
||||
CYRF_ConfigRFChannel(hopping_frequency[phase - J6PRO_CHAN_1]);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
CYRF_WriteDataPacket(packet);
|
||||
if (phase == J6PRO_CHAN_4) {
|
||||
phase = J6PRO_CHAN_1;
|
||||
return 13900;
|
||||
}
|
||||
phase = phase + 1;
|
||||
return 3550;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void J6PRO_init()
|
||||
{
|
||||
j6pro_cyrf_init();
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
phase = J6PRO_BIND;
|
||||
else
|
||||
phase = J6PRO_CHANSEL;
|
||||
}
|
||||
|
||||
#endif
|
||||
186
Multiprotocol/JJRC345_nrf24l01.ino
Normal file
186
Multiprotocol/JJRC345_nrf24l01.ino
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with JJRC345
|
||||
|
||||
#if defined(JJRC345_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
//#define JJRC345_FORCE_ID
|
||||
|
||||
#define JJRC345_PACKET_PERIOD 7450 // Timeout for callback in uSec
|
||||
#define JJRC345_INITIAL_WAIT 500
|
||||
#define JJRC345_PACKET_SIZE 16
|
||||
#define JJRC345_RF_BIND_CHANNEL 5
|
||||
#define SKYTMBLR_RF_BIND_CHANNEL 40
|
||||
#define JJRC345_BIND_COUNT 500
|
||||
#define JJRC345_NUM_CHANNELS 4
|
||||
|
||||
|
||||
enum JJRC345_FLAGS {
|
||||
// flags going to packet[8]
|
||||
JJRC345_FLAG_HEADLESS = 0x40,
|
||||
JJRC345_FLAG_RTH = 0x80,
|
||||
// flags going to packet[9]
|
||||
SKYTMBLR_FLAG_UNK1 = 0x40,
|
||||
SKYTMBLR_FLAG_UNK2 = 0x80,
|
||||
// flags going to packet[10]
|
||||
SKYTMBLR_FLAG_LED = 0x40,
|
||||
SKYTMBLR_FLAG_UNK3 = 0x80,
|
||||
};
|
||||
|
||||
static uint8_t __attribute__((unused)) JJRC345_convert_channel(uint8_t num)
|
||||
{
|
||||
uint8_t val=convert_channel_8b(num);
|
||||
// 7E..60..41..01, 80 center, 81..C1..E0..FE
|
||||
if(val<0x80)
|
||||
{
|
||||
val=0x80-val; // 80..01
|
||||
if(val>0x7E)
|
||||
val=0x7E; // 7E..01
|
||||
}
|
||||
else if(val>0xFE)
|
||||
val=0xFE; // 81..FE
|
||||
return val;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) JJRC345_send_packet()
|
||||
{
|
||||
packet[0] = 0x00;
|
||||
packet[2] = 0x00;
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{ //00 05 00 0A 46 4A 41 47 00 00 40 46 A5 4A F1 18
|
||||
packet[1] = (sub_protocol == JJRC345 ? JJRC345_RF_BIND_CHANNEL:SKYTMBLR_RF_BIND_CHANNEL);
|
||||
packet[4] = hopping_frequency[0];
|
||||
packet[5] = hopping_frequency[1];
|
||||
packet[6] = hopping_frequency[2];
|
||||
packet[7] = hopping_frequency[3];
|
||||
packet[12] = 0xa5;
|
||||
}
|
||||
else
|
||||
{ //00 41 00 0A 00 80 80 80 00 00 40 46 00 49 F1 18
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
hopping_frequency_no++;
|
||||
hopping_frequency_no %= JJRC345_NUM_CHANNELS;
|
||||
packet[1] = hopping_frequency[hopping_frequency_no]; // next packet will be sent on this channel
|
||||
|
||||
packet[4] = convert_channel_8b(THROTTLE); // throttle: 00..FF
|
||||
packet[5] = JJRC345_convert_channel(RUDDER); // rudder: 70..60..41..01, 80 center, 81..C1..E0..F0
|
||||
packet[6] = JJRC345_convert_channel(ELEVATOR); // elevator: 70..60..41..01, 80 center, 81..C1..E0..F0
|
||||
packet[7] = JJRC345_convert_channel(AILERON); // aileron: 70..60..41..01, 80 center, 81..C1..E0..F0
|
||||
|
||||
if(CH5_SW) //Flip
|
||||
{
|
||||
if(packet[6]>0xF0)
|
||||
packet[6]=0xFF;
|
||||
else if(packet[6]<0x80 && packet[6]>0x70)
|
||||
packet[6]=0x7F;
|
||||
if(packet[7]>0xF0)
|
||||
packet[7]=0xFF;
|
||||
else if(packet[7]<0x80 && packet[7]>0x70)
|
||||
packet[7]=0x7F;
|
||||
}
|
||||
|
||||
packet[12] = 0x02; // Rate: 00-01-02
|
||||
}
|
||||
|
||||
packet[3] = 0x00; // Checksum upper bits
|
||||
|
||||
packet[8] = 0x00 // Rudder trim, 00 when not used, 01..1F when trimmed left, 20..3F
|
||||
| GET_FLAG(CH6_SW ,JJRC345_FLAG_HEADLESS) // 0x40 HeadLess
|
||||
| GET_FLAG(CH7_SW ,JJRC345_FLAG_RTH); // 0x80 RTH
|
||||
packet[9] = 0x00 // Elevator trim, 00 when not used, 20..25 when trimmed up, 0..1F when trimmed down
|
||||
| GET_FLAG(CH9_SW ,SKYTMBLR_FLAG_UNK1) // 0x40 Unknown
|
||||
| GET_FLAG(CH10_SW,SKYTMBLR_FLAG_UNK2); // 0x80 Unknown
|
||||
packet[10] = 0x00 // Aileron trim, 00 when not used, 00..1F when trimmed left, 21..3F when trimmed right
|
||||
| GET_FLAG(!CH8_SW,SKYTMBLR_FLAG_LED) // 0x40 LED
|
||||
| GET_FLAG(CH11_SW,SKYTMBLR_FLAG_UNK3); // 0x80 Unknown
|
||||
|
||||
packet[11] = hopping_frequency[0]; // First hopping frequency
|
||||
|
||||
// Checksum
|
||||
uint16_t sum=2;
|
||||
for (uint8_t i = 0; i < 13; i++)
|
||||
sum += packet[i];
|
||||
packet[13]=sum;
|
||||
packet[3]=((sum>>8)<<2)+2;
|
||||
|
||||
// TX ID
|
||||
packet[14] = rx_tx_addr[2];
|
||||
packet[15] = rx_tx_addr[3];
|
||||
|
||||
// Power on, TX mode
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, JJRC345_PACKET_SIZE);
|
||||
|
||||
NRF24L01_SetPower(); // Set tx_power
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) JJRC345_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, sub_protocol == JJRC345 ? JJRC345_RF_BIND_CHANNEL:SKYTMBLR_RF_BIND_CHANNEL); // Bind channel
|
||||
}
|
||||
|
||||
uint16_t JJRC345_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(JJRC345_PACKET_PERIOD);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter==0)
|
||||
BIND_DONE;
|
||||
}
|
||||
JJRC345_send_packet();
|
||||
return JJRC345_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) JJRC345_initialize_txid()
|
||||
{
|
||||
calc_fh_channels(JJRC345_NUM_CHANNELS);
|
||||
|
||||
#ifdef JJRC345_FORCE_ID
|
||||
//TX 1
|
||||
rx_tx_addr[2]=0x1B;
|
||||
rx_tx_addr[3]=0x12;
|
||||
hopping_frequency[0] = 0x3f;
|
||||
hopping_frequency[1] = 0x49;
|
||||
hopping_frequency[2] = 0x47;
|
||||
hopping_frequency[3] = 0x47;
|
||||
//TX 2
|
||||
rx_tx_addr[2]=0xF1;
|
||||
rx_tx_addr[3]=0x18;
|
||||
hopping_frequency[0] = 0x46;
|
||||
hopping_frequency[1] = 0x4A;
|
||||
hopping_frequency[2] = 0x41;
|
||||
hopping_frequency[3] = 0x47;
|
||||
#endif
|
||||
}
|
||||
|
||||
void JJRC345_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = JJRC345_BIND_COUNT;
|
||||
JJRC345_initialize_txid();
|
||||
JJRC345_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
115
Multiprotocol/Joysway_a7105.ino
Normal file
115
Multiprotocol/Joysway_a7105.ino
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(JOYSWAY_A7105_INO)
|
||||
|
||||
#include "iface_a7105.h"
|
||||
|
||||
//#define JOYSWAY_FORCE_ID
|
||||
|
||||
static void __attribute__((unused)) JOYSWAY_send_packet()
|
||||
{
|
||||
static uint8_t next_ch = 0x30;
|
||||
|
||||
//RF frequency
|
||||
if (packet_count == 254)
|
||||
{
|
||||
packet_count = 0;
|
||||
A7105_WriteID(0x5475c52a);
|
||||
rf_ch_num = 0x0a;
|
||||
}
|
||||
else if (packet_count == 2)
|
||||
{
|
||||
A7105_WriteID(MProtocol_id);
|
||||
rf_ch_num = 0x30;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet_count & 0x01)
|
||||
rf_ch_num = 0x30;
|
||||
else
|
||||
rf_ch_num = next_ch;
|
||||
}
|
||||
if (! (packet_count & 0x01))
|
||||
{
|
||||
next_ch++;
|
||||
if (next_ch >= 0x45)
|
||||
next_ch = 0x30;
|
||||
}
|
||||
|
||||
//Payload
|
||||
packet[0] = packet_count == 0 ? 0xdd : 0xff;
|
||||
//ID
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
packet[5] = 0x00;
|
||||
//Channels
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
packet[ 6 + (i & 0x01) + ((i & 0x02)<<1)] = convert_channel_16b_limit(i, 0x00, 0xCC);
|
||||
packet[8] = 0x64;
|
||||
packet[9] = 0x64;
|
||||
packet[12] = 0x64;
|
||||
packet[13] = 0x64;
|
||||
packet[14] = packet_count == 0 ? 0x30 : 0xaa;
|
||||
//Check
|
||||
uint8_t value = 0;
|
||||
for (uint8_t i = 0; i < 15; i++)
|
||||
value += packet[i];
|
||||
packet[15] = value;
|
||||
|
||||
//Send
|
||||
#if 0
|
||||
debug("ch=%02X P=",rf_ch_num);
|
||||
for(uint8_t i=0; i<16; i++)
|
||||
debug("%02X ", packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
A7105_WriteData(16, rf_ch_num);
|
||||
A7105_SetPower();
|
||||
packet_count++;
|
||||
}
|
||||
|
||||
uint16_t JOYSWAY_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(6000);
|
||||
#endif
|
||||
#ifndef FORCE_JOYSWAY_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
|
||||
JOYSWAY_send_packet();
|
||||
return 6000;
|
||||
}
|
||||
|
||||
void JOYSWAY_init()
|
||||
{
|
||||
BIND_DONE; // not a bind protocol
|
||||
|
||||
MProtocol_id &= 0x00FFFFFF;
|
||||
MProtocol_id |= 0xF8000000;
|
||||
#ifdef JOYSWAY_FORCE_ID
|
||||
MProtocol_id = 0xf82dcaa0;
|
||||
#endif
|
||||
|
||||
set_rx_tx_addr(MProtocol_id);
|
||||
|
||||
A7105_Init();
|
||||
|
||||
packet_count = 2;
|
||||
}
|
||||
#endif
|
||||
111
Multiprotocol/KF606_ccnrf.ino
Normal file
111
Multiprotocol/KF606_ccnrf.ino
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Compatible with KF606 plane.
|
||||
|
||||
#if defined(KF606_CCNRF_INO)
|
||||
|
||||
#include "iface_nrf250k.h"
|
||||
|
||||
//#define FORCE_KF606_ORIGINAL_ID
|
||||
|
||||
#define KF606_INITIAL_WAIT 500
|
||||
#define KF606_PACKET_PERIOD 3000
|
||||
#define KF606_RF_BIND_CHANNEL 7
|
||||
#define KF606_PAYLOAD_SIZE 4
|
||||
#define KF606_BIND_COUNT 857 //3sec
|
||||
#define KF606_RF_NUM_CHANNELS 2
|
||||
|
||||
static void __attribute__((unused)) KF606_send_packet()
|
||||
{
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0xAA;
|
||||
memcpy(&packet[1],rx_tx_addr,3);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[0]= 0x55;
|
||||
packet[1]= convert_channel_8b(THROTTLE); // 0..255
|
||||
// Deadband is needed on aileron, 40 gives +-6%
|
||||
packet[2]=convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, Low rates:50..80..AF, High rates:3E..80..C1
|
||||
// Aileron trim must be on a separated channel C1..D0..DF
|
||||
packet[3]= convert_channel_16b_limit(CH5,0xC1,0xDF);
|
||||
}
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
XN297L_Hopping(hopping_frequency_no);
|
||||
hopping_frequency_no ^= 1; // 2 RF channels
|
||||
}
|
||||
|
||||
XN297L_WritePayload(packet, KF606_PAYLOAD_SIZE);
|
||||
|
||||
XN297L_SetPower(); // Set tx_power
|
||||
XN297L_SetFreqOffset(); // Set frequency offset
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) KF606_initialize_txid()
|
||||
{
|
||||
rx_tx_addr[0]=rx_tx_addr[3]; // Use RX_num;
|
||||
hopping_frequency[0]=(rx_tx_addr[0]&0x3F)+9;
|
||||
hopping_frequency[1]=hopping_frequency[0]+3;
|
||||
#ifdef FORCE_KF606_ORIGINAL_ID
|
||||
//TX1
|
||||
rx_tx_addr[0]=0x57;
|
||||
rx_tx_addr[1]=0x02;
|
||||
rx_tx_addr[2]=0x00;
|
||||
hopping_frequency[0]=0x20;
|
||||
hopping_frequency[0]=0x23;
|
||||
//TX2
|
||||
rx_tx_addr[0]=0x25;
|
||||
rx_tx_addr[1]=0x04;
|
||||
rx_tx_addr[2]=0x00;
|
||||
hopping_frequency[0]=0x2E;
|
||||
hopping_frequency[0]=0x31;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) KF606_RF_init()
|
||||
{
|
||||
XN297L_Init();
|
||||
XN297L_SetTXAddr((uint8_t*)"\xe7\xe7\xe7\xe7\xe7", 5);
|
||||
XN297L_HoppingCalib(KF606_RF_NUM_CHANNELS); // Calibrate all channels
|
||||
XN297L_RFChannel(KF606_RF_BIND_CHANNEL); // Set bind channel
|
||||
}
|
||||
|
||||
uint16_t KF606_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(KF606_PACKET_PERIOD);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
{
|
||||
BIND_DONE;
|
||||
XN297L_SetTXAddr(rx_tx_addr, 3);
|
||||
}
|
||||
KF606_send_packet();
|
||||
return KF606_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void KF606_init()
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
KF606_initialize_txid();
|
||||
KF606_RF_init();
|
||||
hopping_frequency_no = 0;
|
||||
bind_counter=KF606_BIND_COUNT;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,263 +12,324 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// Last sync with hexfet new_protocols/KN_nrf24l01.c dated 2015-11-09
|
||||
|
||||
#if defined(KN_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define KN_BIND_COUNT 1000 // for KN 2sec every 2ms - 1000 packets
|
||||
// Timeout for callback in uSec, 2ms=2000us for KN
|
||||
#define KN_PACKET_PERIOD 2000
|
||||
#define KN_PACKET_CHKTIME 100 // Time to wait for packet to be sent (no ACK, so very short)
|
||||
// Wait for RX chip stable - 10ms
|
||||
#define KN_INIT_WAIT_MS 10000
|
||||
|
||||
//#define PAYLOADSIZE 16
|
||||
#define NFREQCHANNELS 4
|
||||
#define KN_TXID_SIZE 4
|
||||
//Payload(16 bytes) plus overhead(10 bytes) is 208 bits, takes about 0.4ms or 0.2ms
|
||||
//to send for the rate of 500kb/s and 1Mb/s respectively.
|
||||
|
||||
// Callback timeout period for sending bind packets, minimum 250
|
||||
#define KN_BINDING_PACKET_PERIOD 1000
|
||||
|
||||
// Timeout for sending data packets, in uSec, KN protocol requires 2ms
|
||||
#define KN_WL_SENDING_PACKET_PERIOD 2000
|
||||
// Timeout for sending data packets, in uSec, KNFX protocol requires 1.2 ms
|
||||
#define KN_FX_SENDING_PACKET_PERIOD 1200
|
||||
|
||||
// packets to be sent during binding, last 0.5 seconds in WL Toys and 0.2 seconds in Feilun
|
||||
#define KN_WL_BIND_COUNT 500
|
||||
#define KN_FX_BIND_COUNT 200
|
||||
|
||||
#define KN_PAYLOADSIZE 16
|
||||
|
||||
//24L01 has 126 RF channels, can we use all of them?
|
||||
#define KN_MAX_RF_CHANNEL 73
|
||||
|
||||
//KN protocol for WL Toys changes RF frequency every 10 ms, repeats with only 4 channels.
|
||||
//Feilun variant uses only 2 channels, so we will just repeat the hopping channels later
|
||||
#define KN_RF_CH_COUNT 4
|
||||
|
||||
//KN protocol for WL Toys sends 4 data packets every 2ms per frequency, plus a 2ms gap.
|
||||
#define KN_WL_PACKET_SEND_COUNT 5
|
||||
//KN protocol for Feilun sends 8 data packets every 1.2ms per frequency, plus a 0.3ms gap.
|
||||
#define KN_FX_PACKET_SEND_COUNT 8
|
||||
|
||||
#define KN_TX_ADDRESS_SIZE 5
|
||||
|
||||
enum {
|
||||
KN_FLAG_DR = 0x01, // Dual Rate
|
||||
KN_FLAG_TH = 0x02, // Throttle Hold
|
||||
KN_FLAG_IDLEUP = 0x04, // Idle up
|
||||
KN_PHASE_PRE_BIND,
|
||||
KN_PHASE_BINDING,
|
||||
KN_PHASE_PRE_SEND,
|
||||
KN_PHASE_SENDING,
|
||||
};
|
||||
|
||||
enum {
|
||||
KN_FLAG_DR = 0x01, // Dual Rate: 1 - full range
|
||||
KN_FLAG_TH = 0x02, // Throttle Hold: 1 - hold
|
||||
KN_FLAG_IDLEUP = 0x04, // Idle up: 1 - 3D
|
||||
KN_FLAG_RES1 = 0x08,
|
||||
KN_FLAG_RES2 = 0x10,
|
||||
KN_FLAG_RES3 = 0x20,
|
||||
KN_FLAG_GYRO3 = 0x40, // 00 - 6G mode, 01 - 3G mode
|
||||
KN_FLAG_GYRO3 = 0x40, // 0 - 6G mode, 1 - 3G mode
|
||||
KN_FLAG_GYROR = 0x80 // Always 0 so far
|
||||
};
|
||||
|
||||
//
|
||||
enum {
|
||||
KN_INIT2 = 0,
|
||||
KN_INIT2_NO_BIND,
|
||||
KN_BIND,
|
||||
KN_DATA
|
||||
};
|
||||
|
||||
/*enum {
|
||||
USE1MBPS_NO = 0,
|
||||
USE1MBPS_YES = 1,
|
||||
};*/
|
||||
|
||||
// 2-bytes CRC
|
||||
#define CRC_CONFIG (BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO))
|
||||
|
||||
void kn_init()
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function init 24L01 regs and packet data for binding
|
||||
// Send tx address, hopping table (for Wl Toys), and data rate to the KN receiver during binding.
|
||||
// It seems that KN can remember these parameters, no binding needed after power up.
|
||||
// Bind uses fixed TX address "KNDZK", 1 Mbps data rate and channel 83
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) kn_bind_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG);
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknoledgement
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5-byte RX/TX address
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0); // Disable retransmit
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 0x20); // bytes of data payload for pipe 0
|
||||
|
||||
|
||||
NRF24L01_Activate(0x73); // Activate feature register
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
|
||||
// Enable: Dynamic Payload Length, Payload with ACK , W_TX_PAYLOAD_NOACK
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, BV(NRF2401_1D_EN_DPL) | BV(NRF2401_1D_EN_ACK_PAY) | BV(NRF2401_1D_EN_DYN_ACK));
|
||||
NRF24L01_Activate(0x73);
|
||||
}
|
||||
|
||||
uint16_t kn_init2()
|
||||
{
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
packet_sent = 0;
|
||||
packet_count = 0;
|
||||
hopping_frequency_no = 0;
|
||||
|
||||
// Turn radio power on
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, CRC_CONFIG | BV(NRF24L01_00_PWR_UP));
|
||||
return 150;
|
||||
}
|
||||
|
||||
void set_tx_for_bind()
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 83);
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps for binding
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"KNDZK", 5);
|
||||
}
|
||||
packet[0] = 'K';
|
||||
packet[1] = 'N';
|
||||
packet[2] = 'D';
|
||||
packet[3] = 'Z';
|
||||
//Use first four bytes of tx_addr
|
||||
packet[4] = rx_tx_addr[0];
|
||||
packet[5] = rx_tx_addr[1];
|
||||
packet[6] = rx_tx_addr[2];
|
||||
packet[7] = rx_tx_addr[3];
|
||||
|
||||
void set_tx_for_data()
|
||||
if(sub_protocol==WLTOYS)
|
||||
{
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
packet[8] = hopping_frequency[0];
|
||||
packet[9] = hopping_frequency[1];
|
||||
packet[10] = hopping_frequency[2];
|
||||
packet[11] = hopping_frequency[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[8] = 0x00;
|
||||
packet[9] = 0x00;
|
||||
packet[10] = 0x00;
|
||||
packet[11] = 0x00;
|
||||
}
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x01; //(USE1MBPS_YES) ? 0x01 : 0x00;
|
||||
|
||||
//Set RF channel
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 83);
|
||||
}
|
||||
|
||||
void kn_calc_fh_channels(uint32_t seed)
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// Update control data to be sent
|
||||
// Do it once per frequency, so the same values will be sent 4 times
|
||||
// KN uses 4 10-bit data channels plus a 8-bit switch channel
|
||||
//
|
||||
// The packet[0] is used for pitch/throttle, the relation is hard coded, not changeable.
|
||||
// We can change the throttle/pitch range though.
|
||||
//
|
||||
// How to use trim? V977 stock controller can trim 6-axis mode to eliminate the drift.
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) kn_update_packet_control_data()
|
||||
{
|
||||
uint16_t value;
|
||||
value = convert_channel_10b(THROTTLE, false);
|
||||
packet[0] = (value >> 8) & 0xFF;
|
||||
packet[1] = value & 0xFF;
|
||||
value = convert_channel_10b(AILERON, false);
|
||||
packet[2] = (value >> 8) & 0xFF;
|
||||
packet[3] = value & 0xFF;
|
||||
value = convert_channel_10b(ELEVATOR, false);
|
||||
packet[4] = (value >> 8) & 0xFF;
|
||||
packet[5] = value & 0xFF;
|
||||
value = convert_channel_10b(RUDDER, false);
|
||||
packet[6] = (value >> 8) & 0xFF;
|
||||
packet[7] = value & 0xFF;
|
||||
// Trims, middle is 0x64 (100) range 0-200
|
||||
packet[8] = convert_channel_16b_limit(CH9,0,200); // 0x64; // T
|
||||
packet[9] = convert_channel_16b_limit(CH10,0,200); // 0x64; // A
|
||||
packet[10] = convert_channel_16b_limit(CH11,0,200); // 0x64; // E
|
||||
packet[11] = 0x64; // R
|
||||
|
||||
flags=0;
|
||||
if (CH5_SW)
|
||||
flags = KN_FLAG_DR;
|
||||
if (CH6_SW)
|
||||
flags |= KN_FLAG_TH;
|
||||
if (CH7_SW)
|
||||
flags |= KN_FLAG_IDLEUP;
|
||||
if (CH8_SW)
|
||||
flags |= KN_FLAG_GYRO3;
|
||||
|
||||
packet[12] = flags;
|
||||
|
||||
packet[13] = 0x00;
|
||||
if(sub_protocol==WLTOYS)
|
||||
packet[13] = (packet_sent << 5) | (hopping_frequency_no << 2);
|
||||
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x00;
|
||||
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function generate RF TX packet address
|
||||
// V977 can remember the binding parameters; we do not need rebind when power up.
|
||||
// This requires the address must be repeatable for a specific RF ID at power up.
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) kn_calculate_tx_addr()
|
||||
{
|
||||
if(sub_protocol==FEILUN)
|
||||
{
|
||||
uint8_t addr2;
|
||||
// Generate TXID with sum of minimum 256 and maximum 256+MAX_RF_CHANNEL-32
|
||||
rx_tx_addr[1] = 1 + rx_tx_addr[0] % (KN_MAX_RF_CHANNEL-33);
|
||||
addr2 = 1 + rx_tx_addr[2] % (KN_MAX_RF_CHANNEL-33);
|
||||
if ((uint16_t)(rx_tx_addr[0] + rx_tx_addr[1]) < 256)
|
||||
rx_tx_addr[2] = addr2;
|
||||
else
|
||||
rx_tx_addr[2] = 0x00;
|
||||
rx_tx_addr[3] = 0x00;
|
||||
while((uint16_t)(rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2] + rx_tx_addr[3]) < 257)
|
||||
rx_tx_addr[3] += addr2;
|
||||
}
|
||||
//The 5th byte is a constant, must be 'K'
|
||||
rx_tx_addr[4] = 'K';
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function generates "random" RF hopping frequency channel numbers.
|
||||
// These numbers must be repeatable for a specific seed
|
||||
// The generated number range is from 0 to MAX_RF_CHANNEL. No repeat or adjacent numbers
|
||||
//
|
||||
// For Feilun variant, the channels are calculated from TXID, and since only 2 channels are used
|
||||
// we copy them to fill up to MAX_RF_CHANNEL
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) kn_calculate_freqency_hopping_channels()
|
||||
{
|
||||
if(sub_protocol==WLTOYS)
|
||||
{
|
||||
uint8_t idx = 0;
|
||||
uint32_t rnd = seed;
|
||||
while (idx < NFREQCHANNELS) {
|
||||
uint32_t rnd = MProtocol_id;
|
||||
while (idx < KN_RF_CH_COUNT)
|
||||
{
|
||||
uint8_t i;
|
||||
rnd = rnd * 0x0019660D + 0x3C6EF35F; // Randomization
|
||||
|
||||
// Use least-significant byte. 73 is prime, so channels 76..77 are unused
|
||||
uint8_t next_ch = ((rnd >> 8) % 73) + 2;
|
||||
uint8_t next_ch = ((rnd >> 8) % KN_MAX_RF_CHANNEL) + 2;
|
||||
// Keep the distance 2 between the channels - either odd or even
|
||||
if (((next_ch ^ seed) & 0x01 )== 0)
|
||||
if (((next_ch ^ MProtocol_id) & 0x01 )== 0)
|
||||
continue;
|
||||
// Check that it's not duplicate and spread uniformly
|
||||
for (i = 0; i < idx; i++) {
|
||||
for (i = 0; i < idx; i++)
|
||||
if(hopping_frequency[i] == next_ch)
|
||||
break;
|
||||
}
|
||||
if (i != idx)
|
||||
continue;
|
||||
hopping_frequency[idx++] = next_ch;
|
||||
}
|
||||
}
|
||||
|
||||
void kn_initialize_tx_id()
|
||||
{
|
||||
rx_tx_addr[4] = 'K';
|
||||
kn_calc_fh_channels(MProtocol_id);
|
||||
}
|
||||
|
||||
#define PACKET_COUNT_SHIFT 5
|
||||
#define RF_CHANNEL_SHIFT 2
|
||||
void kn_send_packet(uint8_t bind)
|
||||
{
|
||||
uint8_t rf_ch;
|
||||
if (bind) {
|
||||
rf_ch = 83;
|
||||
packet[0] = 'K';
|
||||
packet[1] = 'N';
|
||||
packet[2] = 'D';
|
||||
packet[3] = 'Z';
|
||||
packet[4] = rx_tx_addr[0];
|
||||
packet[5] = rx_tx_addr[1];
|
||||
packet[6] = rx_tx_addr[2];
|
||||
packet[7] = rx_tx_addr[3];
|
||||
packet[8] = hopping_frequency[0];
|
||||
packet[9] = hopping_frequency[1];
|
||||
packet[10] = hopping_frequency[2];
|
||||
packet[11] = hopping_frequency[3];
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x01; //mode_bitrate == USE1MBPS_YES ? 0x01 : 0x00;
|
||||
} else {
|
||||
rf_ch = hopping_frequency[hopping_frequency_no];
|
||||
|
||||
// Each packet is repeated 4 times on the same channel
|
||||
// We're not strictly repeating them, rather we
|
||||
// send new packet on the same frequency, so the
|
||||
// receiver gets the freshest command. As receiver
|
||||
// hops to a new frequency as soon as valid packet
|
||||
// received it does not matter that the packet is
|
||||
// not the same one repeated twice - nobody checks this
|
||||
|
||||
// NB! packet_count overflow is handled and used in
|
||||
// callback.
|
||||
if (++packet_count == 4)
|
||||
hopping_frequency_no = (hopping_frequency_no + 1) & 0x03;
|
||||
|
||||
uint16_t kn_throttle, kn_rudder, kn_elevator, kn_aileron;
|
||||
kn_throttle = convert_channel_10b(THROTTLE);
|
||||
kn_aileron = convert_channel_10b(AILERON);
|
||||
kn_elevator = convert_channel_10b(ELEVATOR);
|
||||
kn_rudder = convert_channel_10b(RUDDER);
|
||||
packet[0] = (kn_throttle >> 8) & 0xFF;
|
||||
packet[1] = kn_throttle & 0xFF;
|
||||
packet[2] = (kn_aileron >> 8) & 0xFF;
|
||||
packet[3] = kn_aileron & 0xFF;
|
||||
packet[4] = (kn_elevator >> 8) & 0xFF;
|
||||
packet[5] = kn_elevator & 0xFF;
|
||||
packet[6] = (kn_rudder >> 8) & 0xFF;
|
||||
packet[7] = kn_rudder & 0xFF;
|
||||
// Trims, middle is 0x64 (100) 0-200
|
||||
packet[8] = 0x64; // T
|
||||
packet[9] = 0x64; // A
|
||||
packet[10] = 0x64; // E
|
||||
packet[11] = 0x64; // R
|
||||
|
||||
if (Servo_data[AUX1] > PPM_SWITCH)
|
||||
flags |= KN_FLAG_DR;
|
||||
else
|
||||
flags=0;
|
||||
if (Servo_data[AUX2] > PPM_SWITCH)
|
||||
flags |= KN_FLAG_TH;
|
||||
if (Servo_data[AUX3] > PPM_SWITCH)
|
||||
flags |= KN_FLAG_IDLEUP;
|
||||
if (Servo_data[AUX4] > PPM_SWITCH)
|
||||
flags |= KN_FLAG_GYRO3;
|
||||
|
||||
packet[12] = flags;
|
||||
|
||||
packet[13] = (packet_count << PACKET_COUNT_SHIFT) | (hopping_frequency_no << RF_CHANNEL_SHIFT);
|
||||
|
||||
packet[14] = 0x00;
|
||||
packet[15] = 0x00;
|
||||
}
|
||||
|
||||
packet_sent = 0;
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch);
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, 16);
|
||||
//++total_packets;
|
||||
packet_sent = 1;
|
||||
|
||||
if (! hopping_frequency_no) {
|
||||
//Keep transmit power updated
|
||||
NRF24L01_SetPower();
|
||||
{//FEILUN
|
||||
hopping_frequency[0] = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2] + rx_tx_addr[3]; // - 256; ???
|
||||
hopping_frequency[1] = hopping_frequency[0] + 32;
|
||||
hopping_frequency[2] = hopping_frequency[0];
|
||||
hopping_frequency[3] = hopping_frequency[1];
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t kn_callback()
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function setup 24L01
|
||||
// V977 uses one way communication, receiving only. 24L01 RX is never enabled.
|
||||
// V977 needs payload length in the packet. We should configure 24L01 to enable Packet Control Field(PCF)
|
||||
// Some RX reg settings are actually for enable PCF
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void __attribute__((unused)) KN_RF_init()
|
||||
{
|
||||
kn_calculate_tx_addr();
|
||||
kn_calculate_freqency_hopping_channels();
|
||||
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 0x20); // bytes of data payload for pipe 0
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 1); // Dynamic payload for data pipe 0
|
||||
// Enable: Dynamic Payload Length to enable PCF
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, _BV(NRF2401_1D_EN_DPL));
|
||||
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
// Private Functions
|
||||
//================================================================================================
|
||||
void KN_init()
|
||||
{
|
||||
if(sub_protocol==WLTOYS)
|
||||
{
|
||||
packet_period = KN_WL_SENDING_PACKET_PERIOD;
|
||||
bind_counter = KN_WL_BIND_COUNT;
|
||||
packet_count = KN_WL_PACKET_SEND_COUNT;
|
||||
seed = KN_WL_PACKET_SEND_COUNT * KN_WL_SENDING_PACKET_PERIOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_period = KN_FX_SENDING_PACKET_PERIOD;
|
||||
bind_counter = KN_FX_BIND_COUNT;
|
||||
packet_count = KN_FX_PACKET_SEND_COUNT;
|
||||
seed = KN_FX_PACKET_SEND_COUNT * KN_FX_SENDING_PACKET_PERIOD;
|
||||
}
|
||||
KN_RF_init();
|
||||
phase = IS_BIND_IN_PROGRESS ? KN_PHASE_PRE_BIND : KN_PHASE_PRE_SEND;
|
||||
}
|
||||
|
||||
uint16_t KN_callback()
|
||||
{
|
||||
uint16_t timeout = KN_PACKET_PERIOD;
|
||||
switch (phase)
|
||||
{
|
||||
case KN_INIT2:
|
||||
bind_counter = KN_BIND_COUNT;
|
||||
timeout = kn_init2();
|
||||
phase = KN_BIND;
|
||||
set_tx_for_bind();
|
||||
break;
|
||||
case KN_INIT2_NO_BIND:
|
||||
timeout = kn_init2();
|
||||
phase = KN_DATA;
|
||||
set_tx_for_data();
|
||||
break;
|
||||
case KN_BIND:
|
||||
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED)
|
||||
return KN_PACKET_CHKTIME;
|
||||
kn_send_packet(1);
|
||||
if (--bind_counter == 0) {
|
||||
phase = KN_DATA;
|
||||
set_tx_for_data();
|
||||
case KN_PHASE_PRE_BIND:
|
||||
kn_bind_init();
|
||||
phase=KN_PHASE_BINDING;
|
||||
//Do once, no break needed
|
||||
case KN_PHASE_BINDING:
|
||||
if(bind_counter>0)
|
||||
{
|
||||
bind_counter--;
|
||||
NRF24L01_WritePayload(packet, KN_PAYLOADSIZE);
|
||||
return KN_BINDING_PACKET_PERIOD;
|
||||
}
|
||||
BIND_DONE;
|
||||
//Continue
|
||||
case KN_PHASE_PRE_SEND:
|
||||
packet_sent = 0;
|
||||
hopping_frequency_no = 0;
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, KN_TX_ADDRESS_SIZE);
|
||||
phase = KN_PHASE_SENDING;
|
||||
//Do once, no break needed
|
||||
case KN_PHASE_SENDING:
|
||||
if(packet_sent >= packet_count)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(seed);
|
||||
#endif
|
||||
packet_sent = 0;
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no >= KN_RF_CH_COUNT) hopping_frequency_no = 0;
|
||||
kn_update_packet_control_data();
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
}
|
||||
break;
|
||||
case KN_DATA:
|
||||
if (packet_count == 4)
|
||||
packet_count = 0;
|
||||
else {
|
||||
if (packet_sent && NRF24L01_packet_ack() != PKT_ACKED)
|
||||
return KN_PACKET_CHKTIME;
|
||||
kn_send_packet(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return timeout;
|
||||
else
|
||||
{
|
||||
// Update sending count and RF channel index.
|
||||
// The protocol sends 4 data packets every 2ms per frequency, plus a 2ms gap.
|
||||
// Each data packet need a packet number and RF channel index
|
||||
packet[13] = 0x00;
|
||||
if(sub_protocol==WLTOYS)
|
||||
packet[13] = (packet_sent << 5) | (hopping_frequency_no << 2);
|
||||
}
|
||||
NRF24L01_WritePayload(packet, KN_PAYLOADSIZE);
|
||||
packet_sent++;
|
||||
return packet_period;
|
||||
} //switch
|
||||
|
||||
uint16_t initKN(){
|
||||
//total_packets = 0;
|
||||
//mode_bitrate=USE1MBPS_YES;
|
||||
kn_init();
|
||||
phase = IS_AUTOBIND_FLAG_on ? KN_INIT2 : KN_INIT2_NO_BIND;
|
||||
kn_initialize_tx_id();
|
||||
|
||||
// Call callback in 50ms
|
||||
return 50000;
|
||||
//Bad things happened, reset
|
||||
packet_sent = 0;
|
||||
phase = KN_PHASE_PRE_SEND;
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
201
Multiprotocol/Kyosho_a7105.ino
Normal file
201
Multiprotocol/Kyosho_a7105.ino
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(KYOSHO_A7105_INO)
|
||||
|
||||
#include "iface_a7105.h"
|
||||
|
||||
//#define KYOSHO_FORCE_ID_FHSS
|
||||
//#define KYOSHO_FORCE_ID_HYPE
|
||||
|
||||
//Kyosho constants & variables
|
||||
#define KYOSHO_BIND_COUNT 2500
|
||||
|
||||
static void __attribute__((unused)) KYOSHO_send_packet()
|
||||
{
|
||||
//ID
|
||||
packet[1] = rx_tx_addr[0];
|
||||
packet[2] = rx_tx_addr[1];
|
||||
packet[3] = rx_tx_addr[2];
|
||||
packet[4] = rx_tx_addr[3];
|
||||
//unknown may be RX ID on some other remotes
|
||||
memset(packet+5,0xFF,4);
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[ 0] = 0xBC; // bind indicator
|
||||
packet[ 9] &= 0x01;
|
||||
packet[ 9] ^= 0x01; // high/ low part of the RF table
|
||||
packet[10] = 0x00;
|
||||
//RF table
|
||||
for(uint8_t i=0; i<16;i++)
|
||||
packet[i+11]=hopping_frequency[i+(packet[9]<<4)];
|
||||
//unknwon
|
||||
packet[27] = 0x05;
|
||||
packet[28] = 0x00;
|
||||
memset(packet+29,0xFF,8);
|
||||
//frequency hop during bind
|
||||
if(packet[9])
|
||||
rf_ch_num=0x8C;
|
||||
else
|
||||
rf_ch_num=0x0D;
|
||||
}
|
||||
else
|
||||
{
|
||||
packet[ 0] = 0x58; // normal packet
|
||||
//14 channels: steering, throttle, ...
|
||||
for(uint8_t i = 0; i < 14; i++)
|
||||
{
|
||||
uint16_t temp=convert_channel_ppm(i);
|
||||
packet[9 + i*2]=temp&0xFF; // low byte of servo timing(1000-2000us)
|
||||
packet[10 + i*2]=(temp>>8)&0xFF; // high byte of servo timing(1000-2000us)
|
||||
}
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no];
|
||||
hopping_frequency_no++;
|
||||
packet[34] |= (hopping_frequency_no&0x0F)<<4;
|
||||
packet[36] |= (hopping_frequency_no&0xF0); // last byte is ending with F on the dumps so let's see
|
||||
hopping_frequency_no &= 0x1F;
|
||||
}
|
||||
#if 0
|
||||
debug("ch=%02X P=",rf_ch_num);
|
||||
for(uint8_t i=0; i<37; i++)
|
||||
debug("%02X ", packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
A7105_WriteData(37, rf_ch_num);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) KYOSHO_hype_send_packet()
|
||||
{
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
if(packet_sent==0)
|
||||
{//build the packet and send it
|
||||
packet[0] = rx_tx_addr[1];
|
||||
packet[1] = rx_tx_addr[3];
|
||||
//RF table
|
||||
for(uint8_t i=0; i<15;i++)
|
||||
packet[i+2]=hopping_frequency[i];
|
||||
A7105_WriteData(17, 0x01);
|
||||
packet_sent++;
|
||||
packet_period=1421;
|
||||
#if 0
|
||||
debug("ch=01 P=");
|
||||
for(uint8_t i=0; i<17; i++)
|
||||
debug("%02X ", packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
A7105_Strobe(A7105_TX); //only send
|
||||
}
|
||||
else
|
||||
{
|
||||
//original TX is only refreshing the packet every 20ms and keep repeating the same packet in between (STROBE_TX)
|
||||
//build packet=6 channels with order AETR
|
||||
for(uint8_t i=0;i<6;i++)
|
||||
packet[i] = convert_channel_8b(CH_AETR[i]);
|
||||
//set RF channel
|
||||
rf_ch_num=hopping_frequency[hopping_frequency_no];
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no>14)
|
||||
hopping_frequency_no = 0;
|
||||
//send it
|
||||
A7105_WriteData(6, rf_ch_num);
|
||||
packet_period=931; //packet period fluctuates a lot on the original TX from one packet to the other but stable if looking over a period of 40ms
|
||||
#if 0
|
||||
debug("ch=%02X P=",rf_ch_num);
|
||||
for(uint8_t i=0; i<6; i++)
|
||||
debug("%02X ", packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t KYOSHO_callback()
|
||||
{
|
||||
#ifndef FORCE_KYOSHO_TUNING
|
||||
A7105_AdjustLOBaseFreq(1);
|
||||
#endif
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter==0)
|
||||
{
|
||||
BIND_DONE;
|
||||
if(sub_protocol==KYOSHO_HYPE)
|
||||
{
|
||||
A7105_WriteID(MProtocol_id);
|
||||
A7105_WriteReg(A7105_03_FIFOI,0x05);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hopping_frequency_no==0)
|
||||
A7105_SetPower();
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
}
|
||||
if(sub_protocol==KYOSHO_FHSS)
|
||||
KYOSHO_send_packet();
|
||||
else//HYPE
|
||||
KYOSHO_hype_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void KYOSHO_init()
|
||||
{
|
||||
A7105_Init();
|
||||
|
||||
// compute channels from ID
|
||||
calc_fh_channels(sub_protocol==KYOSHO_FHSS?32:15);
|
||||
hopping_frequency_no=0;
|
||||
|
||||
#ifdef KYOSHO_FORCE_ID_FHSS
|
||||
if(sub_protocol==KYOSHO_FHSS)
|
||||
{
|
||||
memcpy(rx_tx_addr,"\x3A\x39\x37\x00",4);
|
||||
memcpy(hopping_frequency,"\x29\x4C\x67\x92\x31\x1C\x77\x18\x23\x6E\x81\x5C\x8F\x5A\x51\x94\x7A\x12\x45\x6C\x7F\x1E\x0D\x88\x63\x8C\x4F\x37\x26\x61\x2C\x8A",32);
|
||||
}
|
||||
#endif
|
||||
if(sub_protocol==KYOSHO_HYPE)
|
||||
{
|
||||
MProtocol_id &= 0x00FF00FF;
|
||||
rx_tx_addr[0] = 0xAF - (rx_tx_addr[1]&0x0F);
|
||||
rx_tx_addr[2] = 0xFF - rx_tx_addr[3];
|
||||
MProtocol_id |= (rx_tx_addr[0]<<24) + (rx_tx_addr[2]<<8);
|
||||
#ifdef KYOSHO_FORCE_ID_HYPE
|
||||
MProtocol_id=0xAF90738C;
|
||||
set_rx_tx_addr(MProtocol_id);
|
||||
memcpy(hopping_frequency,"\x27\x1B\x63\x75\x03\x39\x57\x69\x87\x0F\x7B\x3F\x33\x51\x6F",15);
|
||||
#endif
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
A7105_WriteID(0xAF00FF00);
|
||||
else
|
||||
{
|
||||
A7105_WriteID(MProtocol_id);
|
||||
A7105_WriteReg(A7105_03_FIFOI,0x05);
|
||||
}
|
||||
}
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
bind_counter = KYOSHO_BIND_COUNT;
|
||||
|
||||
packet_sent=0;
|
||||
packet_period=3852; //FHSS
|
||||
}
|
||||
#endif
|
||||
300
Multiprotocol/LOLI_nrf24l01.ino
Normal file
300
Multiprotocol/LOLI_nrf24l01.ino
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(LOLI_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define LOLI_BIND_CHANNEL 33
|
||||
#define LOLI_PACKET_SIZE 11
|
||||
#define LOLI_NUM_CHANNELS 5
|
||||
|
||||
static void __attribute__((unused)) LOLI_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"LOVE!", 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"LOVE!", 5);
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, LOLI_PACKET_SIZE); // RX FIFO size
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps
|
||||
}
|
||||
|
||||
// flags going to packet[1] for packet type 0xa2 (Rx config)
|
||||
#define LOLI_FLAG_PWM7 0x02
|
||||
#define LOLI_FLAG_PWM2 0x04
|
||||
#define LOLI_FLAG_PWM1 0x08
|
||||
#define LOLI_FLAG_SBUS 0x40
|
||||
#define LOLI_FLAG_PPM 0x80
|
||||
|
||||
// flags going to packet[2] for packet type 0xa2 (Rx config)
|
||||
#define LOLI_FLAG_SW8 0x01
|
||||
#define LOLI_FLAG_SW7 0x02
|
||||
#define LOLI_FLAG_SW6 0x04
|
||||
#define LOLI_FLAG_SW5 0x08
|
||||
#define LOLI_FLAG_SW4 0x10
|
||||
#define LOLI_FLAG_SW3 0x20
|
||||
#define LOLI_FLAG_SW2 0x40
|
||||
#define LOLI_FLAG_SW1 0x80
|
||||
|
||||
#ifdef LOLI_NRF24L01_INO
|
||||
uint8_t LOLI_P1, LOLI_P2;
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) LOLI_send_packet()
|
||||
{
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet[0] = 0xa0;
|
||||
memcpy(&packet[1], hopping_frequency, LOLI_NUM_CHANNELS);
|
||||
memcpy(&packet[6], rx_tx_addr, 5);
|
||||
rf_ch_num = LOLI_BIND_CHANNEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Check RX config
|
||||
uint8_t P1=0;
|
||||
uint8_t P2=0;
|
||||
//ch1: PWM/PPM
|
||||
if(Channel_data[CH1+8] > CHANNEL_MAX_COMMAND)
|
||||
P1|=LOLI_FLAG_PWM1;
|
||||
else if(Channel_data[CH1+8] > CHANNEL_SWITCH)
|
||||
P1|=LOLI_FLAG_PPM;
|
||||
//ch2: PWM
|
||||
if(Channel_data[CH2+8] > CHANNEL_MAX_COMMAND)
|
||||
P1|=LOLI_FLAG_PWM2;
|
||||
//ch5: SBUS
|
||||
if(Channel_data[CH5+8] > CHANNEL_SWITCH)
|
||||
P1|=LOLI_FLAG_SBUS;
|
||||
//ch7: PWM
|
||||
if(Channel_data[CH7+8] > CHANNEL_MAX_COMMAND)
|
||||
P1|=LOLI_FLAG_PWM7;
|
||||
|
||||
//switches
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
if(Channel_data[i+8]<CHANNEL_MIN_COMMAND)
|
||||
P2 |= 1 << (7-i);
|
||||
|
||||
if(LOLI_P1!=P1 || LOLI_P2!=P2)
|
||||
flags=10;
|
||||
if(flags)
|
||||
{// Send RX config since P1 or P2 have changed
|
||||
LOLI_P1=P1;LOLI_P2=P2;
|
||||
packet[0] = 0xa2;
|
||||
packet[1] = LOLI_P1; // CH1:LOLI_FLAG_PPM || LOLI_FLAG_PWM1, CH2:LOLI_FLAG_PWM2, CH5:LOLI_FLAG_SBUS, CH7:LOLI_FLAG_PWM7
|
||||
packet[2] = LOLI_P2; // CHx switch bit(8-x)=1
|
||||
flags--;
|
||||
}
|
||||
else
|
||||
{// Normal packet
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
packet[0] = IS_FAILSAFE_VALUES_on ? 0xa0 : 0xa1;
|
||||
#else
|
||||
packet[0] = 0xa1;
|
||||
#endif
|
||||
|
||||
//Build channels
|
||||
uint8_t ch=0, offset=1;
|
||||
uint16_t val;
|
||||
for(uint8_t i=0;i<2;i++)
|
||||
{
|
||||
val = convert_channel_10b(ch++, IS_FAILSAFE_VALUES_on);
|
||||
packet[offset++] = val >> 2;
|
||||
packet[offset ] = val << 6;
|
||||
val = convert_channel_10b(ch++, IS_FAILSAFE_VALUES_on);
|
||||
packet[offset++]|= val >> 4;
|
||||
packet[offset ] = val << 4;
|
||||
val = convert_channel_10b(ch++, IS_FAILSAFE_VALUES_on);
|
||||
packet[offset++]|= val >> 6;
|
||||
packet[offset ] = val << 2;
|
||||
val = convert_channel_10b(ch++, IS_FAILSAFE_VALUES_on);
|
||||
packet[offset++]|= val >> 8;
|
||||
packet[offset++] = val & 0xff;
|
||||
}
|
||||
FAILSAFE_VALUES_off; // Failsafe values are sent if they were available
|
||||
}
|
||||
|
||||
if (++hopping_frequency_no > LOLI_NUM_CHANNELS-1)
|
||||
hopping_frequency_no = 0;
|
||||
rf_ch_num = hopping_frequency[hopping_frequency_no];
|
||||
}
|
||||
|
||||
#if 0
|
||||
debug("P(%02X):",rf_ch_num);
|
||||
for(uint8_t i=0; i<LOLI_PACKET_SIZE; i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
|
||||
//Send packet
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num);
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0a); // 8bit CRC, TX
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_WritePayload(packet, LOLI_PACKET_SIZE);
|
||||
}
|
||||
|
||||
enum{
|
||||
LOLI_BIND1,
|
||||
LOLI_BIND2,
|
||||
LOLI_BIND3,
|
||||
LOLI_PREP_DATA,
|
||||
LOLI_DATA1,
|
||||
LOLI_DATA2,
|
||||
LOLI_SET_RX_CONFIG,
|
||||
LOLI_SET_FAILSAFE
|
||||
};
|
||||
|
||||
#define LOLI_WRITE_TIME 1000
|
||||
|
||||
uint16_t LOLI_callback()
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case LOLI_BIND1:
|
||||
if(bind_counter)
|
||||
if(--bind_counter==0)
|
||||
{
|
||||
phase=LOLI_PREP_DATA;
|
||||
break;
|
||||
}
|
||||
// send bind packet
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0a); // 8bit CRC, TX
|
||||
LOLI_send_packet();
|
||||
phase++;
|
||||
return 2000;
|
||||
case LOLI_BIND2:
|
||||
// switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x3b); // 8bit CRC, RX
|
||||
phase++;
|
||||
packet_count = 0;
|
||||
return 2000;
|
||||
case LOLI_BIND3:
|
||||
// got bind response ?
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{
|
||||
NRF24L01_ReadPayload(packet, LOLI_PACKET_SIZE);
|
||||
if (packet[0] == 'O' && packet[1] == 'K')
|
||||
{
|
||||
debugln("Bind OK");
|
||||
phase++; // LOLI_PREP_DATA
|
||||
break;
|
||||
}
|
||||
}
|
||||
packet_count++;
|
||||
if (packet_count > 50)
|
||||
phase = LOLI_BIND1;
|
||||
return 1000;
|
||||
|
||||
case LOLI_PREP_DATA:
|
||||
BIND_DONE;
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, rx_tx_addr, 5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, rx_tx_addr, 5);
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushRx();
|
||||
packet_count = 0;
|
||||
//defaut RX config with servo outputs
|
||||
LOLI_P1=0;LOLI_P2=0;flags=10;
|
||||
phase++;
|
||||
|
||||
case LOLI_DATA1:
|
||||
#ifdef LOLI_HUB_TELEMETRY
|
||||
// Check telemetry
|
||||
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
NRF24L01_ReadPayload(packet, LOLI_PACKET_SIZE);
|
||||
#if 0
|
||||
debug("T:");
|
||||
for(uint8_t i=0; i<LOLI_PACKET_SIZE; i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
RX_RSSI = packet[0]<<1;
|
||||
uint16_t val=((packet[1] << 8) | packet[2])/10;
|
||||
if(val > 255) val=255;
|
||||
v_lipo1 = val;
|
||||
val=((packet[3] << 8) | packet[4])/10;
|
||||
if(val > 255) val=255;
|
||||
v_lipo2 = val;
|
||||
telemetry_link = 1;
|
||||
telemetry_counter++; // TX LQI counter
|
||||
if(telemetry_lost)
|
||||
{
|
||||
telemetry_lost = 0;
|
||||
packet_count = 100;
|
||||
telemetry_counter = 100;
|
||||
}
|
||||
}
|
||||
//LQI
|
||||
packet_count++;
|
||||
if(packet_count>=100)
|
||||
{
|
||||
packet_count=0;
|
||||
TX_LQI=telemetry_counter;
|
||||
if(telemetry_counter==0)
|
||||
telemetry_lost = 1;
|
||||
telemetry_counter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Send data packet
|
||||
LOLI_send_packet();
|
||||
|
||||
#ifdef LOLI_HUB_TELEMETRY
|
||||
phase ++;
|
||||
return LOLI_WRITE_TIME;
|
||||
case LOLI_DATA2:
|
||||
// Wait for packet to be sent
|
||||
while( (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS)) == 0);
|
||||
// Switch to RX mode
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x3b); // 8bit CRC, RX
|
||||
phase = LOLI_DATA1;
|
||||
return 20000 - LOLI_WRITE_TIME;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return 20000;
|
||||
}
|
||||
|
||||
void LOLI_init()
|
||||
{
|
||||
rx_tx_addr[1] %= 0x30;
|
||||
calc_fh_channels(LOLI_NUM_CHANNELS);
|
||||
for (uint8_t i=0; i < LOLI_NUM_CHANNELS; i++)
|
||||
if (hopping_frequency[i] == LOLI_BIND_CHANNEL)
|
||||
hopping_frequency[i]++;
|
||||
|
||||
if (IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
bind_counter=250;
|
||||
phase = LOLI_BIND1;
|
||||
}
|
||||
else
|
||||
phase = LOLI_PREP_DATA;
|
||||
|
||||
LOLI_RF_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
352
Multiprotocol/MJXQ_nrf24l01.ino
Normal file
352
Multiprotocol/MJXQ_nrf24l01.ino
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with MJX WLH08, X600, X800, H26D, Eachine E010
|
||||
// Last sync with hexfet new_protocols/mjxq_nrf24l01.c dated 2016-01-17
|
||||
|
||||
#if defined(MJXQ_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
#include "iface_nrf250k.h"
|
||||
|
||||
#define MJXQ_BIND_COUNT 150
|
||||
#define MJXQ_PACKET_PERIOD 4000 // Timeout for callback in uSec
|
||||
#define MJXQ_INITIAL_WAIT 500
|
||||
#define MJXQ_PACKET_SIZE 16
|
||||
#define MJXQ_RF_NUM_CHANNELS 4
|
||||
#define MJXQ_ADDRESS_LENGTH 5
|
||||
|
||||
// haven't figured out txid<-->rf channel mapping for MJX models
|
||||
const uint8_t PROGMEM MJXQ_map_txid[][3] = {
|
||||
{0xF8, 0x4F, 0x1C},
|
||||
{0xC8, 0x6E, 0x02},
|
||||
{0x48, 0x6A, 0x40} };
|
||||
const uint8_t PROGMEM MJXQ_map_rfchan[][4] = {
|
||||
{0x0A, 0x46, 0x3A, 0x42},
|
||||
{0x0A, 0x3C, 0x36, 0x3F},
|
||||
{0x0A, 0x43, 0x36, 0x3F} };
|
||||
|
||||
const uint8_t PROGMEM E010_map_txid[][2] = {
|
||||
{0x4F, 0x1C},
|
||||
{0x90, 0x1C},
|
||||
{0x24, 0x36},
|
||||
{0x7A, 0x40},
|
||||
{0x61, 0x31},
|
||||
{0x5D, 0x37},
|
||||
{0xFD, 0x4F},
|
||||
{0x86, 0x3C},
|
||||
{0x41, 0x22},
|
||||
{0xEE, 0xB3},
|
||||
{0x9A, 0xB2},
|
||||
{0xC0, 0x44},
|
||||
{0x2A, 0xFE},
|
||||
{0xD7, 0x6E},
|
||||
{0x3C, 0xCD}, // for this ID rx_tx_addr[2]=0x01
|
||||
{0xF5, 0x2B} // for this ID rx_tx_addr[2]=0x02
|
||||
};
|
||||
const uint8_t PROGMEM E010_map_rfchan[][2] = {
|
||||
{0x3A, 0x35},
|
||||
{0x2E, 0x36},
|
||||
{0x32, 0x3E},
|
||||
{0x2E, 0x3C},
|
||||
{0x2F, 0x3B},
|
||||
{0x33, 0x3B},
|
||||
{0x33, 0x3B},
|
||||
{0x34, 0x3E},
|
||||
{0x34, 0x2F},
|
||||
{0x39, 0x3E},
|
||||
{0x2E, 0x38},
|
||||
{0x2E, 0x36},
|
||||
{0x2E, 0x38},
|
||||
{0x3A, 0x41},
|
||||
{0x32, 0x3E},
|
||||
{0x33, 0x3F}
|
||||
};
|
||||
|
||||
#define MJXQ_PAN_TILT_COUNT 16 // for H26D - match stock tx timing
|
||||
#define MJXQ_PAN_DOWN 0x08
|
||||
#define MJXQ_PAN_UP 0x04
|
||||
#define MJXQ_TILT_DOWN 0x20
|
||||
#define MJXQ_TILT_UP 0x10
|
||||
|
||||
static uint8_t __attribute__((unused)) MJXQ_pan_tilt_value()
|
||||
{
|
||||
// CH12_SW PAN // H26D
|
||||
// CH13_SW TILT
|
||||
uint8_t pan = 0;
|
||||
packet_count++;
|
||||
if(packet_count & MJXQ_PAN_TILT_COUNT)
|
||||
{
|
||||
if(CH12_SW)
|
||||
pan=MJXQ_PAN_UP;
|
||||
if(Channel_data[CH12]<CHANNEL_MIN_COMMAND)
|
||||
pan=MJXQ_PAN_DOWN;
|
||||
if(CH13_SW)
|
||||
pan+=MJXQ_TILT_UP;
|
||||
if(Channel_data[CH13]<CHANNEL_MIN_COMMAND)
|
||||
pan+=MJXQ_TILT_DOWN;
|
||||
}
|
||||
return pan;
|
||||
}
|
||||
|
||||
#define MJXQ_CHAN2TRIM(X) (((X) & 0x80 ? (X) : 0x7f - (X)) >> 1)
|
||||
static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
|
||||
{
|
||||
packet[0] = convert_channel_8b(THROTTLE);
|
||||
packet[1] = convert_channel_s8b(RUDDER);
|
||||
packet[4] = 0x40; // rudder does not work well with dyntrim
|
||||
packet[2] = 0x80 ^ convert_channel_s8b(ELEVATOR);
|
||||
packet[5] = (CH9_SW || CH14_SW) ? 0x40 : MJXQ_CHAN2TRIM(packet[2]); // trim elevator
|
||||
packet[3] = convert_channel_s8b(AILERON);
|
||||
packet[6] = (CH9_SW || CH14_SW) ? 0x40 : MJXQ_CHAN2TRIM(packet[3]); // trim aileron
|
||||
packet[7] = rx_tx_addr[0];
|
||||
packet[8] = rx_tx_addr[1];
|
||||
packet[9] = rx_tx_addr[2];
|
||||
|
||||
packet[10] = 0x00; // overwritten below for feature bits
|
||||
packet[11] = 0x00; // overwritten below for X600
|
||||
packet[12] = 0x00;
|
||||
packet[13] = 0x00;
|
||||
|
||||
packet[14] = 0xC0; // bind value
|
||||
|
||||
// CH5_SW FLIP
|
||||
// CH6_SW LED / ARM // H26WH - TDR Phoenix mini
|
||||
// CH7_SW PICTURE
|
||||
// CH8_SW VIDEO
|
||||
// CH9_SW HEADLESS
|
||||
// CH10_SW RTH
|
||||
// CH11_SW AUTOFLIP // X800, X600
|
||||
// CH12_SW PAN
|
||||
// CH13_SW TILT
|
||||
// CH14_SW XTRM // Dyntrim, don't use if high.
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case H26WH:
|
||||
case H26D:
|
||||
packet[10]=MJXQ_pan_tilt_value();
|
||||
// fall through on purpose - no break
|
||||
case WLH08:
|
||||
case E010:
|
||||
case PHOENIX:
|
||||
packet[10] += GET_FLAG(CH10_SW, 0x02) //RTH
|
||||
| GET_FLAG(CH9_SW, 0x01); //HEADLESS
|
||||
if (!bind)
|
||||
{
|
||||
packet[14] = 0x04
|
||||
| GET_FLAG(CH5_SW, 0x01) //FLIP
|
||||
| GET_FLAG(CH7_SW, 0x08) //PICTURE
|
||||
| GET_FLAG(CH8_SW, 0x10) //VIDEO
|
||||
| GET_FLAG(!CH6_SW, 0x20); // LED or air/ground mode
|
||||
if(sub_protocol==PHOENIX)
|
||||
{
|
||||
packet[10] |=0x20 //High rate
|
||||
| GET_FLAG(CH6_SW, 0x80); // arm
|
||||
packet[14] &= ~0x24; // unset air/ground & arm flags
|
||||
}
|
||||
if(sub_protocol==H26WH)
|
||||
{
|
||||
packet[10] |=0x40; //High rate
|
||||
packet[14] &= ~0x24; // unset air/ground & arm flags
|
||||
packet[14] |= GET_FLAG(CH6_SW, 0x02); // arm
|
||||
}
|
||||
}
|
||||
break;
|
||||
case X600:
|
||||
packet[10] = GET_FLAG(!CH6_SW, 0x02); //LED
|
||||
packet[11] = GET_FLAG(CH10_SW, 0x01); //RTH
|
||||
if (!bind)
|
||||
{
|
||||
packet[14] = 0x02 // always high rates by bit2 = 1
|
||||
| GET_FLAG(CH5_SW, 0x04) //FLIP
|
||||
| GET_FLAG(CH11_SW, 0x10) //AUTOFLIP
|
||||
| GET_FLAG(CH9_SW, 0x20); //HEADLESS
|
||||
}
|
||||
break;
|
||||
case X800:
|
||||
default:
|
||||
packet[10] = 0x10
|
||||
| GET_FLAG(!CH6_SW, 0x02) //LED
|
||||
| GET_FLAG(CH11_SW, 0x01); //AUTOFLIP
|
||||
if (!bind)
|
||||
{
|
||||
packet[14] = 0x02 // always high rates by bit2 = 1
|
||||
| GET_FLAG(CH5_SW, 0x04) //FLIP
|
||||
| GET_FLAG(CH7_SW, 0x08) //PICTURE
|
||||
| GET_FLAG(CH8_SW, 0x10); //VIDEO
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t sum = packet[0];
|
||||
for (uint8_t i=1; i < MJXQ_PACKET_SIZE-1; i++) sum += packet[i];
|
||||
packet[15] = sum;
|
||||
hopping_frequency_no++;
|
||||
if (sub_protocol == E010 || sub_protocol == PHOENIX)
|
||||
{
|
||||
XN297L_Hopping(hopping_frequency_no / 2);
|
||||
XN297L_SetFreqOffset();
|
||||
XN297L_SetPower();
|
||||
XN297L_WritePayload(packet, MJXQ_PACKET_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no / 2]);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
|
||||
// Power on, TX mode, 2byte CRC and send packet
|
||||
if (sub_protocol == H26D || sub_protocol == H26WH)
|
||||
{
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WritePayload(packet, MJXQ_PACKET_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
|
||||
XN297_WritePayload(packet, MJXQ_PACKET_SIZE);
|
||||
}
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
hopping_frequency_no %= 2 * MJXQ_RF_NUM_CHANNELS; // channels repeated
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MJXQ_RF_init()
|
||||
{
|
||||
uint8_t addr[MJXQ_ADDRESS_LENGTH];
|
||||
memcpy(addr, "\x6d\x6a\x77\x77\x77", MJXQ_ADDRESS_LENGTH);
|
||||
if (sub_protocol == WLH08)
|
||||
memcpy(hopping_frequency, "\x12\x22\x32\x42", MJXQ_RF_NUM_CHANNELS);
|
||||
else
|
||||
if (sub_protocol == H26D || sub_protocol == H26WH || sub_protocol == E010 || sub_protocol == PHOENIX)
|
||||
memcpy(hopping_frequency, "\x2e\x36\x3e\x46", MJXQ_RF_NUM_CHANNELS);
|
||||
else
|
||||
{
|
||||
memcpy(hopping_frequency, "\x0a\x35\x42\x3d", MJXQ_RF_NUM_CHANNELS);
|
||||
memcpy(addr, "\x6d\x6a\x73\x73\x73", MJXQ_ADDRESS_LENGTH);
|
||||
}
|
||||
if (sub_protocol == E010 || sub_protocol == PHOENIX)
|
||||
{
|
||||
XN297L_Init();
|
||||
XN297L_SetTXAddr(addr, sizeof(addr));
|
||||
XN297L_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if (sub_protocol == H26D || sub_protocol == H26WH)
|
||||
{
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, MJXQ_ADDRESS_LENGTH);
|
||||
}
|
||||
else
|
||||
XN297_SetTXAddr(addr, MJXQ_ADDRESS_LENGTH);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, MJXQ_PACKET_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MJXQ_init2()
|
||||
{
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case H26D:
|
||||
memcpy(hopping_frequency, "\x32\x3e\x42\x4e", MJXQ_RF_NUM_CHANNELS);
|
||||
break;
|
||||
case H26WH:
|
||||
memcpy(hopping_frequency, "\x37\x32\x47\x42", MJXQ_RF_NUM_CHANNELS);
|
||||
break;
|
||||
case E010:
|
||||
case PHOENIX:
|
||||
for(uint8_t i=0;i<2;i++)
|
||||
{
|
||||
hopping_frequency[i]=pgm_read_byte_near( &E010_map_rfchan[rx_tx_addr[3]&0x0F][i] );
|
||||
hopping_frequency[i+2]=hopping_frequency[i]+0x10;
|
||||
}
|
||||
XN297L_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
|
||||
break;
|
||||
case WLH08:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
for(uint8_t i=0;i<MJXQ_RF_NUM_CHANNELS;i++)
|
||||
hopping_frequency[i]=pgm_read_byte_near( &MJXQ_map_rfchan[rx_tx_addr[3]%3][i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MJXQ_initialize_txid()
|
||||
{
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case H26WH:
|
||||
memcpy(rx_tx_addr, "\xa4\x03\x00", 3);
|
||||
break;
|
||||
case E010:
|
||||
case PHOENIX:
|
||||
for(uint8_t i=0;i<2;i++)
|
||||
rx_tx_addr[i]=pgm_read_byte_near( &E010_map_txid[rx_tx_addr[3]&0x0F][i] );
|
||||
if((rx_tx_addr[3]&0x0E) == 0x0E)
|
||||
rx_tx_addr[2]=(rx_tx_addr[3]&0x01)+1;
|
||||
else
|
||||
rx_tx_addr[2]=0;
|
||||
break;
|
||||
case WLH08:
|
||||
rx_tx_addr[0]&=0xF8;
|
||||
rx_tx_addr[2]=rx_tx_addr[3]; // Make use of RX_Num
|
||||
break;
|
||||
default:
|
||||
for(uint8_t i=0;i<3;i++)
|
||||
rx_tx_addr[i]=pgm_read_byte_near( &MJXQ_map_txid[rx_tx_addr[3]%3][i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t MJXQ_callback()
|
||||
{
|
||||
if(IS_BIND_DONE)
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(MJXQ_PACKET_PERIOD);
|
||||
#endif
|
||||
MJXQ_send_packet(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
MJXQ_init2();
|
||||
BIND_DONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bind_counter--;
|
||||
MJXQ_send_packet(1);
|
||||
}
|
||||
}
|
||||
|
||||
return MJXQ_PACKET_PERIOD;
|
||||
}
|
||||
|
||||
void MJXQ_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = MJXQ_BIND_COUNT;
|
||||
MJXQ_initialize_txid();
|
||||
MJXQ_RF_init();
|
||||
packet_count=0;
|
||||
}
|
||||
|
||||
#endif
|
||||
608
Multiprotocol/MLINK_cyrf6936.ino
Normal file
608
Multiprotocol/MLINK_cyrf6936.ino
Normal file
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(MLINK_CYRF6936_INO)
|
||||
|
||||
#include "iface_cyrf6936.h"
|
||||
|
||||
//#define MLINK_FORCE_ID
|
||||
#define MLINK_BIND_COUNT 696 // around 20s
|
||||
#define MLINK_NUM_FREQ 78
|
||||
#define MLINK_BIND_CHANNEL 0x01
|
||||
#define MLINK_PACKET_SIZE 8
|
||||
|
||||
enum {
|
||||
MLINK_BIND_TX=0,
|
||||
MLINK_BIND_PREP_RX,
|
||||
MLINK_BIND_RX,
|
||||
MLINK_PREP_DATA,
|
||||
MLINK_SEND1,
|
||||
MLINK_SEND2,
|
||||
MLINK_SEND3,
|
||||
MLINK_CHECK3,
|
||||
MLINK_RX,
|
||||
MLINK_BUILD4,
|
||||
};
|
||||
|
||||
uint8_t MLINK_Data_Code[16], MLINK_CRC_Init, MLINK_Unk_6_2;
|
||||
|
||||
const uint8_t PROGMEM MLINK_init_vals[][2] = {
|
||||
//Init from dump
|
||||
{ CYRF_01_TX_LENGTH, 0x08 }, // Length of packet
|
||||
{ CYRF_02_TX_CTRL, 0x40 }, // Clear TX Buffer
|
||||
{ CYRF_03_TX_CFG, 0x3C }, //0x3E in normal mode, 0x3C in bind mode: SDR 64 chip codes (=8 bytes data code used)
|
||||
{ CYRF_05_RX_CTRL, 0x00 },
|
||||
{ CYRF_06_RX_CFG, 0x93 }, // AGC enabled, overwrite enable, valid flag enable
|
||||
{ CYRF_0B_PWR_CTRL, 0x00 },
|
||||
//{ CYRF_0C_XTAL_CTRL, 0x00 }, // Set to GPIO on reset
|
||||
//{ CYRF_0D_IO_CFG, 0x00 }, // Set to GPIO on reset
|
||||
//{ CYRF_0E_GPIO_CTRL, 0x00 }, // Set by the CYRF_SetTxRxMode function
|
||||
{ CYRF_0F_XACT_CFG, 0x04 }, // end state idle
|
||||
{ CYRF_10_FRAMING_CFG, 0x00 }, // SOP disabled
|
||||
{ CYRF_11_DATA32_THOLD, 0x05 }, // not used???
|
||||
{ CYRF_12_DATA64_THOLD, 0x0F }, // 64 Chip Data PN Code Correlator Threshold
|
||||
{ CYRF_14_EOP_CTRL, 0x05 }, // 5 consecutive noncorrelations symbol for EOP
|
||||
{ CYRF_15_CRC_SEED_LSB, 0x00 }, // not used???
|
||||
{ CYRF_16_CRC_SEED_MSB, 0x00 }, // not used???
|
||||
{ CYRF_1B_TX_OFFSET_LSB,0x00 },
|
||||
{ CYRF_1C_TX_OFFSET_MSB,0x00 },
|
||||
{ CYRF_1D_MODE_OVERRIDE,0x00 },
|
||||
{ CYRF_1E_RX_OVERRIDE, 0x14 }, // RX CRC16 is disabled and Force Receive Data Rate
|
||||
{ CYRF_1F_TX_OVERRIDE, 0x04 }, // TX CRC16 is disabled
|
||||
{ CYRF_26_XTAL_CFG, 0x08 },
|
||||
{ CYRF_29_RX_ABORT, 0x00 },
|
||||
{ CYRF_32_AUTO_CAL_TIME,0x3C },
|
||||
{ CYRF_35_AUTOCAL_OFFSET,0x14 },
|
||||
{ CYRF_39_ANALOG_CTRL, 0x03 }, // Receive invert and all slow
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) MLINK_cyrf_config()
|
||||
{
|
||||
for(uint8_t i = 0; i < sizeof(MLINK_init_vals) / 2; i++)
|
||||
CYRF_WriteRegister(pgm_read_byte_near(&MLINK_init_vals[i][0]), pgm_read_byte_near(&MLINK_init_vals[i][1]));
|
||||
CYRF_WritePreamble(0x333304);
|
||||
CYRF_SetTxRxMode(TX_EN);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MLINK_send_bind_packet()
|
||||
{
|
||||
uint8_t p_c=packet_count>>1;
|
||||
|
||||
memset(packet, p_c<0x16?0x00:0xFF, MLINK_PACKET_SIZE-1);
|
||||
packet[0]=0x0F; // bind
|
||||
packet[1]=p_c;
|
||||
switch(p_c)
|
||||
{
|
||||
case 0x00:
|
||||
packet[2]=0x40; //unknown but seems constant
|
||||
packet[4]=0x01; //unknown but seems constant
|
||||
packet[5]=0x03; //unknown but seems constant
|
||||
packet[6]=0xE3; //unknown but seems constant
|
||||
break;
|
||||
case 0x05:
|
||||
packet[6]=MLINK_CRC_Init; //CRC init value
|
||||
break;
|
||||
case 0x06:
|
||||
packet[2]=MLINK_Unk_6_2; //unknown and different
|
||||
//Start of hopping frequencies
|
||||
for(uint8_t i=0;i<4;i++)
|
||||
packet[i+3]=hopping_frequency[i];
|
||||
break;
|
||||
case 0x15:
|
||||
packet[6]=0x51; //unknown but seems constant
|
||||
break;
|
||||
case 0x16:
|
||||
packet[2]=0x51; //unknown but seems constant
|
||||
packet[3]=0xEC; //unknown but seems constant
|
||||
packet[4]=0x05; //unknown but seems constant
|
||||
break;
|
||||
case 0x1A:
|
||||
packet[1]=0xFF;
|
||||
memset(&packet[2],0x00,5);
|
||||
break;
|
||||
}
|
||||
if(p_c>=0x01 && p_c<=0x04)
|
||||
{//DATA_CODE
|
||||
uint8_t p_c_5=(p_c-1)*5;
|
||||
for(uint8_t i=0;i<5;i++)
|
||||
if(i+p_c_5<16)
|
||||
packet[i+2]=MLINK_Data_Code[i+p_c_5];
|
||||
}
|
||||
else
|
||||
if(p_c>=0x07 && p_c<=0x15)
|
||||
{//Hopping frequencies
|
||||
uint8_t p_c_5=5*(p_c-6)-1;
|
||||
for(uint8_t i=0;i<5;i++)
|
||||
if(i+p_c_5<MLINK_NUM_FREQ)
|
||||
packet[i+2]=hopping_frequency[i+p_c_5];
|
||||
}
|
||||
else
|
||||
if(p_c>0x19)
|
||||
{
|
||||
packet[1]=0xFF;
|
||||
memset(&packet[2], 0x00, MLINK_PACKET_SIZE-3);
|
||||
}
|
||||
|
||||
//Calculate CRC
|
||||
crc8=0xFF; // Init = 0xFF
|
||||
for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++)
|
||||
crc8_update(bit_reverse(packet[i]));
|
||||
packet[7] = bit_reverse(crc8); // CRC reflected out
|
||||
|
||||
//Debug
|
||||
#if 0
|
||||
debug("P(%02d):",p_c);
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
|
||||
//Send packet
|
||||
CYRF_WriteDataPacketLen(packet, MLINK_PACKET_SIZE);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MLINK_send_data_packet()
|
||||
{
|
||||
static uint8_t tog=0;
|
||||
uint8_t start;
|
||||
|
||||
#ifdef FAILSAFE_ENABLE
|
||||
static uint8_t fs=0;
|
||||
if(IS_FAILSAFE_VALUES_on && phase==MLINK_SEND1)
|
||||
{
|
||||
fs=10; // Original radio is sending 70 packets
|
||||
FAILSAFE_VALUES_off;
|
||||
}
|
||||
if(fs)
|
||||
{// Failsafe packets
|
||||
switch(phase)
|
||||
{
|
||||
case MLINK_SEND2:
|
||||
packet[0]=0x06;
|
||||
start=17;
|
||||
break;
|
||||
case MLINK_SEND3:
|
||||
packet[0]=0x84;
|
||||
start=5;
|
||||
fs--;
|
||||
break;
|
||||
default: //MLINK_SEND1:
|
||||
packet[0]=0x05;
|
||||
start=11;
|
||||
break;
|
||||
}
|
||||
//Pack 6 channels per packet
|
||||
for(uint8_t i=0;i<6;i++)
|
||||
{
|
||||
uint8_t val=start<16 ? convert_channel_16b_nolimit(start,426 >> 4,3448 >> 4,true) : 0x00;
|
||||
start--; // switch to next channel
|
||||
packet[i+1]=val;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{// Normal packets
|
||||
if(hopping_frequency_no==0)
|
||||
tog=1;
|
||||
//Channels to be sent
|
||||
if(phase==MLINK_SEND1 || ((hopping_frequency_no%5==0) && (phase==MLINK_SEND2)))
|
||||
{
|
||||
if((hopping_frequency_no&1)==0)
|
||||
packet[0] = 0x09; //10,8,6
|
||||
else
|
||||
packet[0] = 0x01; //11,9,7
|
||||
}
|
||||
else
|
||||
if(phase==MLINK_SEND2)
|
||||
{
|
||||
if(tog)
|
||||
packet[0] = 0x02; //x,15,13
|
||||
else
|
||||
packet[0] = 0x0A; //x,14,12
|
||||
tog^=1;
|
||||
}
|
||||
else
|
||||
{//phase==MLINK_SEND3
|
||||
if((hopping_frequency_no&1)==0)
|
||||
packet[0] = 0x88; //4,2,0
|
||||
else
|
||||
packet[0] = 0x80; //5,3,1
|
||||
}
|
||||
|
||||
//Start channel
|
||||
start=4+6*(packet[0]&3);
|
||||
if((packet[0]&0x08)==0)
|
||||
start++;
|
||||
|
||||
//Channels 426..1937..3448
|
||||
for(uint8_t i=0;i<3;i++)
|
||||
{
|
||||
uint16_t val=start<16 ? convert_channel_16b_nolimit(start,426,3448,false) : 0x0000;
|
||||
start-=2; // switch to next channel
|
||||
packet[i*2+1]=val>>8;
|
||||
packet[i*2+2]=val;
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate CRC
|
||||
crc8=bit_reverse(hopping_frequency_no + MLINK_CRC_Init); // Init = relected freq index + offset
|
||||
for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++)
|
||||
crc8_update(bit_reverse(packet[i]));
|
||||
packet[7] = bit_reverse(crc8); // CRC reflected out
|
||||
|
||||
//Send
|
||||
CYRF_WriteDataPacketLen(packet, MLINK_PACKET_SIZE);
|
||||
|
||||
//Debug
|
||||
#if 0
|
||||
debug("P(%02d):",hopping_frequency_no);
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
debug(" %02X",packet[i]);
|
||||
debugln("");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MLINK_HUB_TELEMETRY
|
||||
static void __attribute__((unused)) MLINK_Send_Telemetry()
|
||||
{ // not sure how MLINK telemetry works, the 2 RXs I have are sending something completly different...
|
||||
telemetry_counter += 2; // TX LQI counter
|
||||
telemetry_link = 1;
|
||||
|
||||
if(packet_in[0]==0x13)
|
||||
{ // RX-9-DR : 13 1A C8 00 01 64 00
|
||||
uint8_t id;
|
||||
for(uint8_t i=1; i<5; i+=3)
|
||||
{//2 sensors per packet
|
||||
id=0x00;
|
||||
switch(packet_in[i]&0x0F)
|
||||
{
|
||||
case 1: //voltage
|
||||
if((packet_in[i]&0xF0) == 0x00)
|
||||
v_lipo1 = packet_in[i+1]; // Rx_Batt*20
|
||||
else
|
||||
v_lipo2 = packet_in[i+1];
|
||||
break;
|
||||
case 2: //current
|
||||
id = 0x28;
|
||||
break;
|
||||
case 3: //vario
|
||||
id = 0x30;
|
||||
break;
|
||||
case 5: //rpm
|
||||
id = 0x03;
|
||||
break;
|
||||
case 6: //temp
|
||||
id = 0x02;
|
||||
break;
|
||||
case 10: //lqi
|
||||
RX_RSSI=RX_LQI=packet_in[i+1]>>1;
|
||||
break;
|
||||
}
|
||||
#if defined HUB_TELEMETRY
|
||||
if(id)
|
||||
{
|
||||
uint16_t val=((packet_in[i+2]&0x80)<<8)|((packet_in[i+2]&0x7F)<<7)|(packet_in[i+1]>>1); //remove the alarm LSB bit, move the sign bit to MSB
|
||||
frsky_send_user_frame(id, val, val>>8);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
if(packet_in[0]==0x03)
|
||||
{ // RX-5 : 03 15 23 00 00 01 02
|
||||
//Incoming packet values
|
||||
RX_RSSI = packet_in[2]<<1; // Looks to be the RX RSSI value
|
||||
RX_LQI = packet_in[5]; // Looks to be connection lost
|
||||
}
|
||||
else
|
||||
RX_RSSI = TX_LQI;
|
||||
|
||||
// Read TX RSSI
|
||||
TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI)&0x1F;
|
||||
|
||||
if(telemetry_lost)
|
||||
{
|
||||
telemetry_lost = 0;
|
||||
packet_count = 50;
|
||||
telemetry_counter = 100;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MLINK_FW_TELEMETRY
|
||||
static void __attribute__((unused)) MLINK_Send_Telemetry()
|
||||
{
|
||||
telemetry_counter += 2; // TX LQI counter
|
||||
telemetry_link = 4;
|
||||
|
||||
// Read TX RSSI
|
||||
TX_RSSI = CYRF_ReadRegister(CYRF_13_RSSI)&0x1F;
|
||||
|
||||
if(telemetry_lost)
|
||||
{
|
||||
telemetry_lost = 0;
|
||||
packet_count = 50;
|
||||
telemetry_counter = 100;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t MLINK_callback()
|
||||
{
|
||||
uint8_t status;
|
||||
uint16_t start;
|
||||
|
||||
switch(phase)
|
||||
{
|
||||
case MLINK_BIND_RX:
|
||||
//debugln("RX");
|
||||
status=CYRF_ReadRegister(CYRF_05_RX_CTRL);
|
||||
if( (status&0x80) == 0 )
|
||||
{//Packet received
|
||||
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
debugln("L=%02X",len)
|
||||
if( len==8 )
|
||||
{
|
||||
CYRF_ReadDataPacketLen(packet, len*2);
|
||||
debug("RX=");
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
debug(" %02X",packet[i*2]);
|
||||
debugln("");
|
||||
//Check CRC
|
||||
crc8=0xFF; // Init = 0xFF
|
||||
for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++)
|
||||
crc8_update(bit_reverse(packet[i<<1]));
|
||||
if(packet[14] == bit_reverse(crc8))
|
||||
{// CRC is ok
|
||||
debugln("CRC ok");
|
||||
if(packet[0]==0x7F)
|
||||
packet_count=3; // Start sending bind payload
|
||||
else if(packet_count > 0x19*2)
|
||||
{
|
||||
if(packet[0] == 0x8F)
|
||||
packet_count++;
|
||||
else if(packet[0] == 0x9F)
|
||||
packet_count=0x80; // End bind
|
||||
else
|
||||
packet_count=0; // Restart bind...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
packet_count=0;
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Enable RX abort
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24); // Force end state
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Disable RX abort
|
||||
phase=MLINK_BIND_TX; // Retry sending bind packet
|
||||
CYRF_SetTxRxMode(TX_EN); // Transmit mode
|
||||
if(packet_count)
|
||||
return 18136;
|
||||
case MLINK_BIND_TX:
|
||||
if(--bind_counter==0 || packet_count>=0x1B*2)
|
||||
{ // Switch to normal mode
|
||||
BIND_DONE;
|
||||
phase=MLINK_PREP_DATA;
|
||||
return 22720;
|
||||
}
|
||||
MLINK_send_bind_packet();
|
||||
if(packet_count == 0 || packet_count > 0x19*2)
|
||||
{
|
||||
phase++; // MLINK_BIND_PREP_RX
|
||||
return 4700; // Original is 4900
|
||||
}
|
||||
packet_count++;
|
||||
if(packet_count&1)
|
||||
return 6000;
|
||||
return 22720;
|
||||
case MLINK_BIND_PREP_RX:
|
||||
start=micros();
|
||||
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 200) // Wait max 200µs for TX to finish
|
||||
if((CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80) == 0x00)
|
||||
break; // Packet transmission complete
|
||||
CYRF_SetTxRxMode(RX_EN); // Receive mode
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x82); // Prepare to receive
|
||||
phase++; //MLINK_BIND_RX
|
||||
if(packet_count > 0x19*2)
|
||||
return 28712; // Give more time to the RX to confirm that the bind is ok...
|
||||
return 28712-4700;
|
||||
|
||||
|
||||
case MLINK_PREP_DATA:
|
||||
CYRF_ConfigDataCode(MLINK_Data_Code,16);
|
||||
MLINK_CRC_Init += 0xED;
|
||||
hopping_frequency_no = 0x00;
|
||||
CYRF_ConfigRFChannel(hopping_frequency[hopping_frequency_no]);
|
||||
CYRF_SetPower(0x38);
|
||||
#if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY)
|
||||
packet_count = 0;
|
||||
telemetry_lost = 1;
|
||||
#endif
|
||||
phase++;
|
||||
|
||||
|
||||
case MLINK_SEND1:
|
||||
MLINK_send_data_packet();
|
||||
phase++;
|
||||
return 4880+1111;
|
||||
case MLINK_SEND2:
|
||||
MLINK_send_data_packet();
|
||||
phase++;
|
||||
if(hopping_frequency_no%5==0)
|
||||
return 4617+1017;
|
||||
return 4617+1422;
|
||||
case MLINK_SEND3:
|
||||
MLINK_send_data_packet();
|
||||
phase++;
|
||||
return 4611;
|
||||
case MLINK_CHECK3:
|
||||
//Switch to next channel
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no>=MLINK_NUM_FREQ)
|
||||
hopping_frequency_no=0;
|
||||
CYRF_ConfigRFChannel(hopping_frequency[hopping_frequency_no]);
|
||||
|
||||
//Receive telemetry
|
||||
if(hopping_frequency_no%5==0)
|
||||
{//Receive telemetry
|
||||
CYRF_SetTxRxMode(RX_EN); // Receive mode
|
||||
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x82); // Prepare to receive
|
||||
phase++; //MLINK_RX
|
||||
return 8038+2434+410-1000;
|
||||
}
|
||||
else
|
||||
CYRF_SetPower(0x38);
|
||||
phase=MLINK_SEND1;
|
||||
return 4470;
|
||||
case MLINK_RX:
|
||||
#if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY)
|
||||
//TX LQI calculation
|
||||
packet_count++;
|
||||
if(packet_count>=50)
|
||||
{
|
||||
packet_count=0;
|
||||
TX_LQI=telemetry_counter;
|
||||
if(telemetry_counter==0)
|
||||
telemetry_lost = 1;
|
||||
telemetry_counter = 0;
|
||||
}
|
||||
#endif
|
||||
status=CYRF_ReadRegister(CYRF_05_RX_CTRL);
|
||||
debug("T(%02X):",status);
|
||||
if( (status&0x80) == 0 )
|
||||
{//Packet received
|
||||
len=CYRF_ReadRegister(CYRF_09_RX_COUNT);
|
||||
debug("(%X)",len)
|
||||
if( len && len <= MLINK_PACKET_SIZE )
|
||||
{
|
||||
CYRF_ReadDataPacketLen(packet_in, len*2);
|
||||
#if defined(MLINK_HUB_TELEMETRY) || defined(MLINK_FW_TELEMETRY)
|
||||
if(len==MLINK_PACKET_SIZE)
|
||||
{
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
//Check CRC
|
||||
crc8=bit_reverse(MLINK_CRC_Init);
|
||||
for(uint8_t i=0;i<MLINK_PACKET_SIZE-1;i++)
|
||||
{
|
||||
packet_in[i]=packet_in[i<<1];
|
||||
crc8_update(bit_reverse(packet_in[i]));
|
||||
debug(" %02X",packet_in[i]);
|
||||
}
|
||||
if(packet_in[14] == bit_reverse(crc8)) // Packet CRC is ok
|
||||
MLINK_Send_Telemetry();
|
||||
else
|
||||
debug(" NOK");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
debugln("");
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Enable RX abort
|
||||
CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24); // Force end state
|
||||
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); // Disable RX abort
|
||||
CYRF_SetTxRxMode(TX_EN); // Transmit mode
|
||||
phase=MLINK_SEND2;
|
||||
return 1000;
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MLINK_shuffle_freqs(uint32_t seed, uint8_t *hop)
|
||||
{
|
||||
randomSeed(seed);
|
||||
|
||||
for(uint8_t i=0; i < MLINK_NUM_FREQ/2; i++)
|
||||
{
|
||||
uint8_t r = random(0xfefefefe) % (MLINK_NUM_FREQ/2);
|
||||
uint8_t tmp = hop[r];
|
||||
hop[r] = hop[i];
|
||||
hop[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void MLINK_init()
|
||||
{
|
||||
MLINK_cyrf_config();
|
||||
|
||||
//Init ID and RF freqs
|
||||
for(uint8_t i=0; i < MLINK_NUM_FREQ/2; i++)
|
||||
{
|
||||
hopping_frequency[i ] = (i<<1) + 3;
|
||||
hopping_frequency[i+MLINK_NUM_FREQ/2] = (i<<1) + 3;
|
||||
}
|
||||
// part1
|
||||
memcpy(MLINK_Data_Code ,rx_tx_addr,4);
|
||||
MLINK_shuffle_freqs(MProtocol_id, hopping_frequency);
|
||||
|
||||
// part2
|
||||
MProtocol_id ^= 0x6FBE3201;
|
||||
set_rx_tx_addr(MProtocol_id);
|
||||
memcpy(MLINK_Data_Code+4,rx_tx_addr,4);
|
||||
MLINK_shuffle_freqs(MProtocol_id, &hopping_frequency[MLINK_NUM_FREQ/2]);
|
||||
|
||||
// part3
|
||||
MLINK_CRC_Init = rx_tx_addr[3]; //value sent during bind then used to init the CRC
|
||||
MLINK_Unk_6_2 = 0x3A; //unknown value sent during bind but doesn't seem to matter
|
||||
|
||||
#ifdef MLINK_FORCE_ID
|
||||
if(RX_num)
|
||||
{
|
||||
//Cockpit SX
|
||||
memcpy(MLINK_Data_Code,"\x4C\x97\x9D\xBF\xB8\x3D\xB5\xBE",8);
|
||||
memcpy(hopping_frequency,"\x0D\x41\x09\x43\x17\x2D\x05\x31\x13\x3B\x1B\x3D\x0B\x41\x11\x45\x09\x2B\x17\x4D\x19\x3F\x03\x3F\x0F\x37\x1F\x47\x1B\x49\x07\x35\x27\x2F\x15\x33\x23\x39\x1F\x33\x19\x45\x0D\x2D\x11\x35\x0B\x47\x25\x3D\x21\x37\x1D\x3B\x05\x2F\x21\x39\x23\x4B\x03\x31\x25\x29\x07\x4F\x1D\x4B\x15\x4D\x13\x4F\x0F\x49\x29\x2B\x27\x43",MLINK_NUM_FREQ);
|
||||
MLINK_Unk_6_2 = 0x3A; //unknown value sent during bind but doesn't seem to matter
|
||||
MLINK_CRC_Init = 0x07; //value sent during bind then used to init the CRC
|
||||
}
|
||||
else
|
||||
{
|
||||
//HFM3
|
||||
memcpy(MLINK_Data_Code,"\xC0\x90\x8F\xBB\x7C\x8E\x2B\x8E",8);
|
||||
memcpy(hopping_frequency,"\x05\x41\x27\x4B\x17\x33\x11\x39\x0F\x3F\x05\x2F\x13\x2D\x25\x31\x1F\x2D\x25\x35\x03\x41\x1B\x43\x09\x3D\x1F\x29\x1D\x35\x0D\x3B\x19\x49\x23\x3B\x17\x47\x1D\x2B\x13\x37\x0B\x31\x23\x33\x29\x3F\x07\x37\x07\x43\x11\x2B\x1B\x39\x0B\x4B\x03\x4F\x21\x47\x0F\x4D\x15\x45\x21\x4F\x09\x3D\x19\x2F\x15\x45\x0D\x49\x27\x4D",MLINK_NUM_FREQ);
|
||||
MLINK_Unk_6_2 = 0x02; //unknown value but doesn't seem to matter
|
||||
MLINK_CRC_Init = 0x3E; //value sent during bind then used to init the CRC
|
||||
}
|
||||
//Other TX
|
||||
//MLINK_Unk_6_2 = 0x7e; //unknown value but doesn't seem to matter
|
||||
//MLINK_CRC_Init = 0xA2; //value sent during bind then used to init the CRC
|
||||
#endif
|
||||
|
||||
for(uint8_t i=0;i<8;i++)
|
||||
MLINK_Data_Code[i+8]=MLINK_Data_Code[7-i];
|
||||
|
||||
debug("ID:")
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
debug(" %02X", MLINK_Data_Code[i]);
|
||||
debugln("");
|
||||
|
||||
debugln("CRC init: %02X", MLINK_CRC_Init)
|
||||
|
||||
debug("RF:")
|
||||
for(uint8_t i=0;i<MLINK_NUM_FREQ;i++)
|
||||
debug(" %02X", hopping_frequency[i]);
|
||||
debugln("");
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
packet_count = 0;
|
||||
bind_counter = MLINK_BIND_COUNT;
|
||||
CYRF_ConfigDataCode((uint8_t*)"\x6F\xBE\x32\x01\xDB\xF1\x2B\x01\xE3\x5C\xFA\x02\x97\x93\xF9\x02",16); //Bind data code
|
||||
CYRF_ConfigRFChannel(MLINK_BIND_CHANNEL);
|
||||
phase = MLINK_BIND_TX;
|
||||
}
|
||||
else
|
||||
phase = MLINK_PREP_DATA;
|
||||
}
|
||||
|
||||
#endif
|
||||
384
Multiprotocol/MT99xx_nrf24l01.ino
Normal file
384
Multiprotocol/MT99xx_nrf24l01.ino
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// compatible with MT99xx, Eachine H7, Yi Zhan i6S and LS114/124
|
||||
// Last sync with Goebish mt99xx_nrf24l01.c dated 2016-01-29
|
||||
|
||||
#if defined(MT99XX_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
#include "iface_nrf250k.h"
|
||||
|
||||
#define MT99XX_BIND_COUNT 928
|
||||
#define MT99XX_PACKET_PERIOD_FY805 2460
|
||||
#define MT99XX_PACKET_PERIOD_MT 2625
|
||||
#define MT99XX_PACKET_PERIOD_YZ 3125
|
||||
#define MT99XX_PACKET_PERIOD_A180 3400 // timing changes between the packets 2 x 27220 then 1x 26080, it seems that it is only on the first RF channel which jitters by 1.14ms but hard to pinpoint with XN297dump
|
||||
#define MT99XX_PACKET_PERIOD_DRAGON 1038 // there is a pause of 2x1038 between the packets 3 and 4 from what XN297dump detects which I think are used for telemetry
|
||||
#define MT99XX_INITIAL_WAIT 500
|
||||
#define MT99XX_PACKET_SIZE 9
|
||||
|
||||
//#define FORCE_A180_ID
|
||||
//#define FORCE_DRAGON_ID
|
||||
|
||||
enum{
|
||||
// flags going to packet[6] (MT99xx, H7)
|
||||
FLAG_MT_RATE1 = 0x01, // (H7 & A180 high rate)
|
||||
FLAG_MT_RATE2 = 0x02, // (MT9916 only)
|
||||
FLAG_MT_VIDEO = 0x10,
|
||||
FLAG_MT_SNAPSHOT= 0x20,
|
||||
FLAG_MT_FLIP = 0x80,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[6] (LS)
|
||||
FLAG_LS_INVERT = 0x01,
|
||||
FLAG_LS_RATE = 0x02,
|
||||
FLAG_LS_HEADLESS= 0x10,
|
||||
FLAG_LS_SNAPSHOT= 0x20,
|
||||
FLAG_LS_VIDEO = 0x40,
|
||||
FLAG_LS_FLIP = 0x80,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[7] (FY805)
|
||||
FLAG_FY805_HEADLESS= 0x10,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[6] (A180)
|
||||
FLAG_A180_3D6G = 0x01,
|
||||
FLAG_A180_RATE = 0x02,
|
||||
};
|
||||
|
||||
enum{
|
||||
// flags going to packet[6] (DRAGON)
|
||||
FLAG_DRAGON_RATE = 0x01,
|
||||
FLAG_DRAGON_RTH = 0x80,
|
||||
FLAG_DRAGON_UNK = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
MT99XX_INIT = 0,
|
||||
MT99XX_BIND,
|
||||
MT99XX_DATA
|
||||
};
|
||||
|
||||
const uint8_t h7_mys_byte[] = {
|
||||
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
|
||||
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
|
||||
};
|
||||
|
||||
const uint8_t ls_mys_byte[] = {
|
||||
0x05, 0x15, 0x25, 0x06, 0x16, 0x26,
|
||||
0x07, 0x17, 0x27, 0x00, 0x10, 0x20,
|
||||
0x01, 0x11, 0x21, 0x02, 0x12, 0x22,
|
||||
0x03, 0x13, 0x23, 0x04, 0x14, 0x24
|
||||
};
|
||||
|
||||
const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60};
|
||||
|
||||
#ifdef DRAGON_HUB_TELEMETRY
|
||||
const uint8_t DRAGON_seq[] = {0x20, 0x60, 0x20, 0x80};
|
||||
#endif
|
||||
|
||||
static void __attribute__((unused)) MT99XX_send_packet()
|
||||
{
|
||||
static uint8_t seq_num=0;
|
||||
|
||||
if(IS_BIND_IN_PROGRESS)
|
||||
{
|
||||
//Bind packet
|
||||
packet[0] = 0x20;
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case YZ:
|
||||
packet_period = MT99XX_PACKET_PERIOD_YZ;
|
||||
packet[1] = 0x15;
|
||||
packet[2] = 0x05;
|
||||
packet[3] = 0x06;
|
||||
break;
|
||||
case LS:
|
||||
packet[1] = 0x14;
|
||||
packet[2] = 0x05;
|
||||
packet[3] = 0x11;
|
||||
break;
|
||||
case FY805:
|
||||
packet_period = MT99XX_PACKET_PERIOD_FY805;
|
||||
packet[1] = 0x15;
|
||||
packet[2] = 0x12;
|
||||
packet[3] = 0x17;
|
||||
break;
|
||||
case A180:
|
||||
packet_period = MT99XX_PACKET_PERIOD_A180;
|
||||
default: // MT99 & H7 & A180 & DRAGON
|
||||
packet[1] = 0x14;
|
||||
packet[2] = 0x03;
|
||||
packet[3] = 0x25;
|
||||
break;
|
||||
}
|
||||
packet[4] = rx_tx_addr[0];
|
||||
packet[5] = rx_tx_addr[1];
|
||||
packet[6] = rx_tx_addr[2];
|
||||
packet[7] = crc8; // checksum offset
|
||||
packet[8] = 0xAA; // fixed
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sub_protocol != YZ)
|
||||
{ // MT99XX & H7 & LS & FY805 & A180 & DRAGON
|
||||
packet[0] = convert_channel_16b_limit(THROTTLE,0xE1,0x00); // throttle
|
||||
packet[1] = convert_channel_16b_limit(RUDDER ,0x00,0xE1); // rudder
|
||||
packet[2] = convert_channel_16b_limit(AILERON ,0xE1,0x00); // aileron
|
||||
packet[3] = convert_channel_16b_limit(ELEVATOR,0x00,0xE1); // elevator
|
||||
packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00)
|
||||
packet[5] = 0x20; // roll trim (0x00-0x20-0x3f)
|
||||
packet[6] = GET_FLAG( CH5_SW, FLAG_MT_FLIP );
|
||||
packet[7] = h7_mys_byte[hopping_frequency_no]; // next rf channel index ?
|
||||
|
||||
switch(sub_protocol)
|
||||
{
|
||||
case MT99:
|
||||
packet[6] |= 0x40 | FLAG_MT_RATE2 // max rate on MT99xx
|
||||
| GET_FLAG( CH7_SW, FLAG_MT_SNAPSHOT )
|
||||
| GET_FLAG( CH8_SW, FLAG_MT_VIDEO );
|
||||
break;
|
||||
case H7:
|
||||
packet[6] |= FLAG_MT_RATE1; // max rate on H7
|
||||
break;
|
||||
case LS:
|
||||
packet[6] |= FLAG_LS_RATE // max rate
|
||||
| GET_FLAG( CH6_SW, FLAG_LS_INVERT ) // invert
|
||||
| GET_FLAG( CH7_SW, FLAG_LS_SNAPSHOT ) // snapshot
|
||||
| GET_FLAG( CH8_SW, FLAG_LS_VIDEO ) // video
|
||||
| GET_FLAG( CH9_SW, FLAG_LS_HEADLESS ); // Headless
|
||||
packet[7] = ls_mys_byte[seq_num++];
|
||||
if(seq_num >= sizeof(ls_mys_byte))
|
||||
seq_num=0;
|
||||
break;
|
||||
case FY805:
|
||||
packet[6]=0x20;
|
||||
//Rate 0x01?
|
||||
//Flip ?
|
||||
packet[7]=0x01
|
||||
|GET_FLAG( CH5_SW, FLAG_MT_FLIP )
|
||||
|GET_FLAG( CH9_SW, FLAG_FY805_HEADLESS ); //HEADLESS
|
||||
crc8=0;
|
||||
break;
|
||||
case A180:
|
||||
packet[6] = FLAG_A180_RATE
|
||||
| GET_FLAG( CH5_SW, FLAG_A180_3D6G );
|
||||
packet[7] = 0x00;
|
||||
break;
|
||||
case DRAGON:
|
||||
if(CH5_SW) // Advanced mode
|
||||
packet[5] |= 0x80;
|
||||
else
|
||||
if(Channel_data[CH5] > CHANNEL_MIN_COMMAND) // Beginner mode
|
||||
packet[5] |= 0x40;
|
||||
packet[6] = FLAG_DRAGON_RATE
|
||||
| GET_FLAG( CH6_SW, FLAG_DRAGON_RTH );
|
||||
|
||||
#ifdef DRAGON_HUB_TELEMETRY
|
||||
//Telemetry
|
||||
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 27 00 00 00 00 60
|
||||
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 28 00 00 00 00 61
|
||||
// C=18 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 24 00 00 00 00 5D
|
||||
// 6C 00 22 = TX address, 27/28/24=vbatt, check = sum(P[0..7]) + AB, where AB comes from? Is it constant?
|
||||
if(hopping_frequency_no == 0)
|
||||
{
|
||||
seq_num++;
|
||||
seq_num &= 0x03;
|
||||
packet_count++;
|
||||
if(packet_count > 11)
|
||||
packet_count = 0;
|
||||
}
|
||||
if(packet_count > 10) // Telemetry packet request every 10 or 11 packets
|
||||
packet[6] |= 0x04; // Request telemetry flag
|
||||
packet[7] = DRAGON_seq[seq_num]; // seq: 20 80 20 60 20 80 20 60... 80 changes to 80+batt from telem
|
||||
if(seq_num==3)
|
||||
packet[7] |= v_lipo1;
|
||||
#else
|
||||
packet[7] = 0x20;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
uint8_t result=crc8;
|
||||
for(uint8_t i=0; i<8; i++)
|
||||
result += packet[i];
|
||||
packet[8] = result;
|
||||
}
|
||||
else
|
||||
{ // YZ
|
||||
packet[0] = convert_channel_16b_limit(THROTTLE,0x00,0x64); // throttle
|
||||
packet[1] = convert_channel_16b_limit(RUDDER ,0x64,0x00); // rudder
|
||||
packet[2] = convert_channel_16b_limit(ELEVATOR,0x00,0x64); // elevator
|
||||
packet[3] = convert_channel_16b_limit(AILERON ,0x64,0x00); // aileron
|
||||
if(packet_count++ >= 23)
|
||||
{
|
||||
seq_num ++;
|
||||
if(seq_num > 2)
|
||||
seq_num = 0;
|
||||
packet_count=0;
|
||||
}
|
||||
packet[4] = yz_p4_seq[seq_num];
|
||||
packet[5] = 0x02 // expert ? (0=unarmed, 1=normal)
|
||||
| GET_FLAG(CH8_SW, 0x10) //VIDEO
|
||||
| GET_FLAG(CH5_SW, 0x80) //FLIP
|
||||
| GET_FLAG(CH9_SW, 0x04) //HEADLESS
|
||||
| GET_FLAG(CH7_SW, 0x20); //SNAPSHOT
|
||||
packet[6] = GET_FLAG(CH6_SW, 0x80); //LED
|
||||
packet[7] = packet[0];
|
||||
for(uint8_t idx = 1; idx < MT99XX_PACKET_SIZE-2; idx++)
|
||||
packet[7] += packet[idx];
|
||||
packet[8] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if(sub_protocol == LS)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x2D); // LS always transmits on the same channel
|
||||
else
|
||||
if(sub_protocol==FY805)
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x4B); // FY805 always transmits on the same channel
|
||||
else // MT99 & H7 & YZ & A180 & DRAGON
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
XN297_WritePayload(packet, MT99XX_PACKET_SIZE);
|
||||
|
||||
if(sub_protocol == DRAGON)
|
||||
{
|
||||
if(hopping_frequency_no == 6)
|
||||
packet_period = 3 * MT99XX_PACKET_PERIOD_DRAGON; // hole probably for the telemetry
|
||||
else
|
||||
packet_period = MT99XX_PACKET_PERIOD_DRAGON;
|
||||
}
|
||||
|
||||
hopping_frequency_no++;
|
||||
if(sub_protocol == YZ || sub_protocol == A180 || sub_protocol == DRAGON )
|
||||
hopping_frequency_no++; // skip every other channel
|
||||
|
||||
if(hopping_frequency_no > 15)
|
||||
hopping_frequency_no = 0;
|
||||
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MT99XX_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
if(sub_protocol == YZ)
|
||||
XN297_SetScrambledMode(XN297_UNSCRAMBLED);
|
||||
XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5);
|
||||
if(sub_protocol == YZ)
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps (nRF24L01+ only)
|
||||
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) );
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) MT99XX_initialize_txid()
|
||||
{
|
||||
rx_tx_addr[1] = rx_tx_addr[3]; // RX_Num
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case YZ:
|
||||
rx_tx_addr[0] = 0x53; // test (SB id)
|
||||
rx_tx_addr[1] = 0x00;
|
||||
rx_tx_addr[2] = 0x00;
|
||||
break;
|
||||
case FY805:
|
||||
rx_tx_addr[0] = 0x81; // test (SB id)
|
||||
rx_tx_addr[1] = 0x0F;
|
||||
rx_tx_addr[2] = 0x00;
|
||||
break;
|
||||
case LS:
|
||||
rx_tx_addr[0] = 0xCC;
|
||||
break;
|
||||
#ifdef FORCE_A180_ID
|
||||
case A180:
|
||||
rx_tx_addr[0] = 0x84; // MikeHRC ID
|
||||
rx_tx_addr[1] = 0x62;
|
||||
rx_tx_addr[2] = 0x4A;
|
||||
//crc8 = 0x30
|
||||
//channel_offset = 0x03;
|
||||
break;
|
||||
#endif
|
||||
#ifdef FORCE_DRAGON_ID
|
||||
case DRAGON:
|
||||
rx_tx_addr[0] = 0x6C; // Laurie ID
|
||||
rx_tx_addr[1] = 0x00;
|
||||
rx_tx_addr[2] = 0x22;
|
||||
//crc8 = 0x8E
|
||||
//channel_offset = 0x06
|
||||
break;
|
||||
#endif
|
||||
default: //MT99 & H7 & A180 & DRAGON
|
||||
rx_tx_addr[2] = 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
rx_tx_addr[3] = 0xCC;
|
||||
rx_tx_addr[4] = 0xCC;
|
||||
|
||||
crc8 = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2];
|
||||
|
||||
//memcpy(hopping_frequency,"\x02\x48\x0C\x3e\x16\x34\x20\x2A\x2A\x20\x34\x16\x3e\x0c\x48\x02",16);
|
||||
for(uint8_t i=0; i<8; i++)
|
||||
{
|
||||
hopping_frequency[(i<<1) ]=0x02 + (10*i);
|
||||
hopping_frequency[(i<<1)+1]=0x48 - (10*i);
|
||||
}
|
||||
hopping_frequency_no=0;
|
||||
}
|
||||
|
||||
uint16_t MT99XX_callback()
|
||||
{
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(packet_period);
|
||||
#endif
|
||||
if(bind_counter)
|
||||
{
|
||||
bind_counter--;
|
||||
if (bind_counter == 0)
|
||||
{
|
||||
// set tx address for data packets
|
||||
XN297_SetTXAddr(rx_tx_addr, 5);
|
||||
// set rf channels
|
||||
uint8_t channel_offset = ((crc8>>4) + (crc8 & 0x0f)) % 8;
|
||||
for(uint8_t i=0;i<16;i++)
|
||||
hopping_frequency[i] += channel_offset;
|
||||
BIND_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
MT99XX_send_packet();
|
||||
return packet_period;
|
||||
}
|
||||
|
||||
void MT99XX_init(void)
|
||||
{
|
||||
if(sub_protocol != A180 && sub_protocol != DRAGON)
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
bind_counter = MT99XX_BIND_COUNT;
|
||||
|
||||
MT99XX_initialize_txid();
|
||||
MT99XX_RF_init();
|
||||
|
||||
packet_period = MT99XX_PACKET_PERIOD_MT;
|
||||
|
||||
packet_count=0;
|
||||
}
|
||||
#endif
|
||||
84
Multiprotocol/Multi.txt
Normal file
84
Multiprotocol/Multi.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
1,Flysky,Flysky,V9x9,V6x6,V912,CX20
|
||||
2,Hubsan,H107,H301,H501
|
||||
3,FrskyD,D8,Cloned
|
||||
4,Hisky,Hisky,HK310
|
||||
5,V2x2,V2x2,JXD506,MR101
|
||||
6,DSM,DSM2_1F,DSM2_2F,DSMX_1F,DSMX_2F,AUTO
|
||||
7,Devo,8CH,10CH,12CH,6CH,7CH
|
||||
8,YD717,YD717,SKYWLKR,SYMAX4,XINXUN,NIHUI
|
||||
9,KN,WLTOYS,FEILUN
|
||||
10,SymaX,SYMAX,SYMAX5C
|
||||
11,SLT,SLT_V1,SLT_V2,Q100,Q200,MR100
|
||||
12,CX10,GREEN,BLUE,DM007,---,J3015_1,J3015_2,MK33041
|
||||
13,CG023,CG023,YD829
|
||||
14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4,QX100
|
||||
15,FrskyX,CH_16,CH_8,EU_16,EU_8,Cloned,Clon_8
|
||||
16,ESky,Std,ET4
|
||||
17,MT99xx,MT,H7,YZ,LS,FY805,A180,Dragon
|
||||
18,MJXq,WLH08,X600,X800,H26D,E010,H26WH,PHOENIX
|
||||
19,Shenqi
|
||||
20,FY326,FY326,FY319
|
||||
21,Futaba,SFHSS
|
||||
22,J6PRO
|
||||
23,FQ777
|
||||
24,ASSAN
|
||||
25,FrskyV
|
||||
26,HONTAI,HONTAI,JJRCX1,X5C1,FQ777_951
|
||||
27,OpnLrs
|
||||
28,AFHDS2A,PWM_IBUS,PPM_IBUS,PWM_SBUS,PPM_SBUS,PWM_IB16,PPM_IB16
|
||||
29,Q2X2,Q222,Q242,Q282
|
||||
30,WK2x01,WK2801,WK2401,W6_5_1,W6_6_1,W6_HEL,W6_HEL_I
|
||||
31,Q303,Q303,CX35,CX10D,CX10WD
|
||||
32,GW008
|
||||
33,DM002
|
||||
34,CABELL,CAB_V3,C_TELEM,-,-,-,-,F_SAFE,UNBIND
|
||||
35,ESKY150,4CH,7CH
|
||||
36,H8_3D,H8_3D,H20H,H20Mini,H30Mini
|
||||
37,CORONA,COR_V1,COR_V2,FD_V3
|
||||
38,CFlie
|
||||
39,Hitec,OPT_FW,OPT_HUB,MINIMA
|
||||
40,WFLY,WFR0x
|
||||
41,BUGS
|
||||
42,BUGSMINI,BUGSMINI,BUGS3H
|
||||
43,Traxxas,RX6519
|
||||
44,NCC1701
|
||||
45,E01X,E012,E015,E016H
|
||||
46,V911S,V911S,E119
|
||||
47,GD00x,GD_V1,GD_V2
|
||||
48,V761,3CH,4CH
|
||||
49,KF606
|
||||
50,Redpine,Fast,Slow
|
||||
51,Potensic,A20
|
||||
52,ZSX,280
|
||||
53,Height,5ch,8ch
|
||||
54,Scanner
|
||||
55,Frsky_RX,Multi,CloneTX,EraseTX,CPPM
|
||||
56,AFHDS2A_RX,Multi,CPPM
|
||||
57,HoTT,Sync,No_Sync
|
||||
58,FX816,P38
|
||||
59,Bayang_RX,Multi,CPPM
|
||||
60,Pelikan,Pro,Lite
|
||||
61,Tiger
|
||||
62,XK,X450,X420
|
||||
63,XN_DUMP,250K,1M,2M,AUTO
|
||||
64,FrskyX2,CH_16,CH_8,EU_16,EU_8,Cloned
|
||||
65,FrSkyR9,915MHz,868MHz,915_8ch,868_8ch,FCC,--,FCC_8ch,--_8ch
|
||||
66,PROPEL,74-Z
|
||||
67,LR12,LR12,LR12_6ch
|
||||
68,Skyartec
|
||||
69,ESKYv2,150V2
|
||||
70,DSM_RX,Multi,CPPM
|
||||
71,JJRC345,JJRC345,SkyTmblr
|
||||
72,Q90C
|
||||
73,Kyosho,FHSS,Hype
|
||||
74,RadioLink,Surface,Air,DumboRC
|
||||
75,---
|
||||
76,Realacc,R11
|
||||
77,OMP
|
||||
78,M-Link
|
||||
79,WFLY,RF20x
|
||||
80,E016H,E016Hv2
|
||||
81,E010r5
|
||||
82,LOLI
|
||||
83,E129
|
||||
84,JOYSWAY
|
||||
91
Multiprotocol/MultiOrange.h
Normal file
91
Multiprotocol/MultiOrange.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//#define ARDUINO_AVR_PRO 1
|
||||
|
||||
#define ORANGE_TX 1
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#define yield()
|
||||
|
||||
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
|
||||
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
|
||||
|
||||
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
|
||||
// the overflow handler is called every 256 ticks.
|
||||
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
|
||||
|
||||
// the whole number of milliseconds per timer0 overflow
|
||||
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
|
||||
|
||||
// the fractional number of milliseconds per timer0 overflow. we shift right
|
||||
// by three to fit these numbers into a byte. (for the clock speeds we care
|
||||
// about - 8 and 16 MHz - this doesn't lose precision.)
|
||||
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
|
||||
#define FRACT_MAX (1000 >> 3)
|
||||
|
||||
#ifndef cbi
|
||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||
#endif
|
||||
#ifndef sbi
|
||||
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||
#endif
|
||||
|
||||
|
||||
void init()
|
||||
{
|
||||
// this needs to be called before setup() or some functions won't
|
||||
// work there
|
||||
|
||||
// Enable external oscillator (16MHz)
|
||||
OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_256CLK_gc ;
|
||||
OSC.CTRL |= OSC_XOSCEN_bm ;
|
||||
while( ( OSC.STATUS & OSC_XOSCRDY_bm ) == 0 )
|
||||
/* wait */ ;
|
||||
// Enable PLL (*2 = 32MHz)
|
||||
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2 ;
|
||||
OSC.CTRL |= OSC_PLLEN_bm ;
|
||||
while( ( OSC.STATUS & OSC_PLLRDY_bm ) == 0 )
|
||||
/* wait */ ;
|
||||
// Switch to PLL clock
|
||||
CPU_CCP = 0xD8 ;
|
||||
CLK.CTRL = CLK_SCLKSEL_RC2M_gc ;
|
||||
CPU_CCP = 0xD8 ;
|
||||
CLK.CTRL = CLK_SCLKSEL_PLL_gc ;
|
||||
|
||||
PMIC.CTRL = 7 ; // Enable all interrupt levels
|
||||
sei();
|
||||
|
||||
#if defined(ADCSRA)
|
||||
// set a2d prescale factor to 128
|
||||
// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
|
||||
// XXX: this will not work properly for other clock speeds, and
|
||||
// this code should use F_CPU to determine the prescale factor.
|
||||
sbi(ADCSRA, ADPS2);
|
||||
sbi(ADCSRA, ADPS1);
|
||||
sbi(ADCSRA, ADPS0);
|
||||
|
||||
// enable a2d conversions
|
||||
sbi(ADCSRA, ADEN);
|
||||
#endif
|
||||
|
||||
// the bootloader connects pins 0 and 1 to the USART; disconnect them
|
||||
// here so they can be used as normal digital i/o; they will be
|
||||
// reconnected in Serial.begin()
|
||||
#if defined(UCSRB)
|
||||
UCSRB = 0;
|
||||
#elif defined(UCSR0B)
|
||||
UCSR0B = 0;
|
||||
#endif
|
||||
|
||||
// Dip Switch inputs
|
||||
PORTA.DIRCLR = 0xFF ;
|
||||
PORTA.PIN0CTRL = 0x18 ;
|
||||
PORTA.PIN1CTRL = 0x18 ;
|
||||
PORTA.PIN2CTRL = 0x18 ;
|
||||
PORTA.PIN3CTRL = 0x18 ;
|
||||
PORTA.PIN4CTRL = 0x18 ;
|
||||
PORTA.PIN5CTRL = 0x18 ;
|
||||
PORTA.PIN6CTRL = 0x18 ;
|
||||
PORTA.PIN7CTRL = 0x18 ;
|
||||
}
|
||||
449
Multiprotocol/Multi_Protos.ino
Normal file
449
Multiprotocol/Multi_Protos.ino
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const char STR_FLYSKY[] ="FlySky";
|
||||
const char STR_HUBSAN[] ="Hubsan";
|
||||
const char STR_FRSKYD[] ="FrSky D";
|
||||
const char STR_HISKY[] ="Hisky";
|
||||
const char STR_V2X2[] ="V2x2";
|
||||
const char STR_DSM[] ="DSM";
|
||||
const char STR_DSM_RX[] ="DSM_RX";
|
||||
const char STR_DEVO[] ="Devo";
|
||||
const char STR_YD717[] ="YD717";
|
||||
const char STR_KN[] ="KN";
|
||||
const char STR_SYMAX[] ="SymaX";
|
||||
const char STR_SLT[] ="SLT";
|
||||
const char STR_CX10[] ="CX10";
|
||||
const char STR_CG023[] ="CG023";
|
||||
const char STR_BAYANG[] ="Bayang";
|
||||
const char STR_FRSKYL[] ="FrSky L";
|
||||
const char STR_FRSKYX[] ="FrSky X";
|
||||
const char STR_FRSKYX2[] ="FrSkyX2";
|
||||
const char STR_ESKY[] ="ESky";
|
||||
const char STR_MT99XX[] ="MT99XX";
|
||||
const char STR_MJXQ[] ="MJXq";
|
||||
const char STR_SHENQI[] ="Shenqi";
|
||||
const char STR_FY326[] ="FY326";
|
||||
const char STR_FUTABA[] ="Futaba";
|
||||
const char STR_J6PRO[] ="J6 Pro";
|
||||
const char STR_JJRC345[] ="JJRC345";
|
||||
const char STR_JOYSWAY[] ="JOYSWAY";
|
||||
const char STR_FQ777[] ="FQ777";
|
||||
const char STR_ASSAN[] ="Assan";
|
||||
const char STR_FRSKYV[] ="FrSky V";
|
||||
const char STR_HONTAI[] ="Hontai";
|
||||
const char STR_AFHDS2A[] ="FlSky2A";
|
||||
const char STR_Q2X2[] ="Q2x2";
|
||||
const char STR_WK2x01[] ="Walkera";
|
||||
const char STR_Q303[] ="Q303";
|
||||
const char STR_Q90C[] ="Q90C";
|
||||
const char STR_GW008[] ="GW008";
|
||||
const char STR_DM002[] ="DM002";
|
||||
const char STR_CABELL[] ="Cabell";
|
||||
const char STR_ESKY150[] ="Esky150";
|
||||
const char STR_ESKY150V2[] ="EskyV2";
|
||||
const char STR_H8_3D[] ="H8 3D";
|
||||
const char STR_CORONA[] ="Corona";
|
||||
const char STR_CFLIE[] ="CFlie";
|
||||
const char STR_HITEC[] ="Hitec";
|
||||
const char STR_WFLY[] ="WFLY";
|
||||
const char STR_WFLY2[] ="WFLY2";
|
||||
const char STR_BUGS[] ="Bugs";
|
||||
const char STR_BUGSMINI[] ="BugMini";
|
||||
const char STR_TRAXXAS[] ="Traxxas";
|
||||
const char STR_NCC1701[] ="NCC1701";
|
||||
const char STR_E01X[] ="E01X";
|
||||
const char STR_V911S[] ="V911S";
|
||||
const char STR_GD00X[] ="GD00x";
|
||||
const char STR_V761[] ="V761";
|
||||
const char STR_KF606[] ="KF606";
|
||||
const char STR_REDPINE[] ="Redpine";
|
||||
const char STR_POTENSIC[] ="Potensi";
|
||||
const char STR_ZSX[] ="ZSX";
|
||||
const char STR_HEIGHT[] ="Height";
|
||||
const char STR_SCANNER[] ="Scanner";
|
||||
const char STR_FRSKY_RX[] ="FrSkyRX";
|
||||
const char STR_AFHDS2A_RX[] ="FS2A_RX";
|
||||
const char STR_HOTT[] ="HoTT";
|
||||
const char STR_FX816[] ="FX816";
|
||||
const char STR_BAYANG_RX[] ="BayanRX";
|
||||
const char STR_PELIKAN[] ="Pelikan";
|
||||
const char STR_TIGER[] ="Tiger";
|
||||
const char STR_XK[] ="XK";
|
||||
const char STR_XN297DUMP[] ="XN297DP";
|
||||
const char STR_FRSKYR9[] ="FrSkyR9";
|
||||
const char STR_PROPEL[] ="Propel";
|
||||
const char STR_SKYARTEC[] ="Skyartc";
|
||||
const char STR_KYOSHO[] ="Kyosho";
|
||||
const char STR_RLINK[] ="RadLink";
|
||||
const char STR_REALACC[] ="Realacc";
|
||||
const char STR_OMP[] ="OMP";
|
||||
const char STR_MLINK[] ="M-Link";
|
||||
const char STR_TEST[] ="Test";
|
||||
const char STR_NANORF[] ="NanoRF";
|
||||
const char STR_E016HV2[] ="E016Hv2";
|
||||
const char STR_E010R5[] ="E010r5";
|
||||
const char STR_LOLI[] ="LOLI";
|
||||
const char STR_E129[] ="E129";
|
||||
|
||||
const char STR_SUBTYPE_FLYSKY[] = "\x04""Std\0""V9x9""V6x6""V912""CX20";
|
||||
const char STR_SUBTYPE_HUBSAN[] = "\x04""H107""H301""H501";
|
||||
const char STR_SUBTYPE_FRSKYD[] = "\x06""D8\0 ""Cloned";
|
||||
const char STR_SUBTYPE_FRSKYX[] = "\x07""D16\0 ""D16 8ch""LBT(EU)""LBT 8ch""Cloned\0""Clo 8ch";
|
||||
const char STR_SUBTYPE_HISKY[] = "\x05""Std\0 ""HK310";
|
||||
const char STR_SUBTYPE_V2X2[] = "\x06""Std\0 ""JXD506""MR101\0";
|
||||
const char STR_SUBTYPE_DSM[] = "\x04""2 1F""2 2F""X 1F""X 2F""Auto";
|
||||
const char STR_SUBTYPE_DEVO[] = "\x04""8ch\0""10ch""12ch""6ch\0""7ch\0";
|
||||
const char STR_SUBTYPE_YD717[] = "\x07""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 ";
|
||||
const char STR_SUBTYPE_KN[] = "\x06""WLtoys""FeiLun";
|
||||
const char STR_SUBTYPE_SYMAX[] = "\x03""Std""X5C";
|
||||
const char STR_SUBTYPE_SLT[] = "\x06""V1_6ch""V2_8ch""Q100\0 ""Q200\0 ""MR100\0";
|
||||
const char STR_SUBTYPE_CX10[] = "\x07""Green\0 ""Blue\0 ""DM007\0 ""-\0 ""JC3015a""JC3015b""MK33041";
|
||||
const char STR_SUBTYPE_CG023[] = "\x05""Std\0 ""YD829";
|
||||
const char STR_SUBTYPE_BAYANG[] = "\x07""Std\0 ""H8S3D\0 ""X16 AH\0""IRDrone""DHD D4\0""QX100\0 ";
|
||||
const char STR_SUBTYPE_MT99[] = "\x06""MT99\0 ""H7\0 ""YZ\0 ""LS\0 ""FY805\0""A180\0 ""Dragon";
|
||||
const char STR_SUBTYPE_MJXQ[] = "\x07""WLH08\0 ""X600\0 ""X800\0 ""H26D\0 ""E010\0 ""H26WH\0 ""Phoenix";
|
||||
const char STR_SUBTYPE_FY326[] = "\x05""Std\0 ""FY319";
|
||||
const char STR_SUBTYPE_HONTAI[] = "\x07""Std\0 ""JJRC X1""X5C1\0 ""FQ_951";
|
||||
const char STR_SUBTYPE_AFHDS2A[] = "\x08""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS""PWM,IB16""PPM,IB16""PWM,SB16""PPM,SB16";
|
||||
const char STR_SUBTYPE_Q2X2[] = "\x04""Q222""Q242""Q282";
|
||||
const char STR_SUBTYPE_WK2x01[] = "\x06""WK2801""WK2401""W6_5_1""W6_6_1""W6_HeL""W6_HeI";
|
||||
const char STR_SUBTYPE_Q303[] = "\x06""Std\0 ""CX35\0 ""CX10D\0""CX10WD";
|
||||
const char STR_SUBTYPE_CABELL[] = "\x07""V3\0 ""V3 Telm""-\0 ""-\0 ""-\0 ""-\0 ""F-Safe\0""Unbind\0";
|
||||
const char STR_SUBTYPE_H83D[] = "\x07""Std\0 ""H20H\0 ""H20Mini""H30Mini";
|
||||
const char STR_SUBTYPE_CORONA[] = "\x05""V1\0 ""V2\0 ""FD V3";
|
||||
const char STR_SUBTYPE_HITEC[] = "\x07""Optima\0""Opt Hub""Minima\0";
|
||||
const char STR_SUBTYPE_BUGS_MINI[] = "\x06""Std\0 ""Bugs3H";
|
||||
const char STR_SUBTYPE_TRAXXAS[] = "\x04""6519";
|
||||
const char STR_SUBTYPE_E01X[] = "\x05""E012\0""E015\0""E016H";
|
||||
const char STR_SUBTYPE_GD00X[] = "\x05""GD_V1""GD_V2";
|
||||
const char STR_SUBTYPE_REDPINE[] = "\x04""Fast""Slow";
|
||||
const char STR_SUBTYPE_POTENSIC[] = "\x03""A20";
|
||||
const char STR_SUBTYPE_ZSX[] = "\x07""280JJRC";
|
||||
const char STR_SUBTYPE_HEIGHT[] = "\x03""5ch""8ch";
|
||||
const char STR_SUBTYPE_FX816[] = "\x03""P38";
|
||||
const char STR_SUBTYPE_XN297DUMP[] = "\x07""250Kbps""1Mbps\0 ""2Mbps\0 ""Auto\0 ""NRF\0 ";
|
||||
const char STR_SUBTYPE_ESKY150[] = "\x03""4ch""7ch";
|
||||
const char STR_SUBTYPE_ESKY150V2[] = "\x05""150V2";
|
||||
const char STR_SUBTYPE_V911S[] = "\x05""V911S""E119\0";
|
||||
const char STR_SUBTYPE_XK[] = "\x04""X450""X420";
|
||||
const char STR_SUBTYPE_FRSKYR9[] = "\x07""915MHz\0""868MHz\0""915 8ch""868 8ch""FCC\0 ""--\0 ""FCC 8ch""-- 8ch\0";
|
||||
const char STR_SUBTYPE_ESKY[] = "\x03""Std""ET4";
|
||||
const char STR_SUBTYPE_PROPEL[] = "\x04""74-Z";
|
||||
const char STR_SUBTYPE_FRSKYL[] = "\x08""LR12\0 ""LR12 6ch";
|
||||
const char STR_SUBTYPE_WFLY[] = "\x05""WFR0x";
|
||||
const char STR_SUBTYPE_WFLY2[] = "\x05""RF20x";
|
||||
const char STR_SUBTYPE_HOTT[] = "\x07""Sync\0 ""No_Sync";
|
||||
const char STR_SUBTYPE_PELIKAN[] = "\x04""Pro\0""Lite";
|
||||
const char STR_SUBTYPE_V761[] = "\x03""3ch""4ch";
|
||||
const char STR_SUBTYPE_RLINK[] = "\x07""Surface""Air\0 ""DumboRC";
|
||||
const char STR_SUBTYPE_REALACC[] = "\x03""R11";
|
||||
const char STR_SUBTYPE_KYOSHO[] = "\x04""FHSS""Hype";
|
||||
const char STR_SUBTYPE_FUTABA[] = "\x05""SFHSS";
|
||||
const char STR_SUBTYPE_JJRC345[] = "\x08""JJRC345\0""SkyTmblr";
|
||||
|
||||
#define NO_SUBTYPE nullptr
|
||||
|
||||
#ifdef SEND_CPPM
|
||||
const char STR_SUB_FRSKY_RX[] = "\x07""Multi\0 ""CloneTX""EraseTX""CPPM\0 ";
|
||||
#define FRCPPM 4
|
||||
const char STR_CPPM[] = "\x05""Multi""CPPM\0";
|
||||
#define NBR_CPPM 2
|
||||
#else
|
||||
const char STR_SUB_FRSKY_RX[] = "\x07""Multi\0 ""CloneTX""EraseTX";
|
||||
#define FRCPPM 3
|
||||
#define STR_CPPM NO_SUBTYPE
|
||||
#define NBR_CPPM 0
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
OPTION_NONE,
|
||||
OPTION_OPTION,
|
||||
OPTION_RFTUNE,
|
||||
OPTION_VIDFREQ,
|
||||
OPTION_FIXEDID,
|
||||
OPTION_TELEM,
|
||||
OPTION_SRVFREQ,
|
||||
OPTION_MAXTHR,
|
||||
OPTION_RFCHAN,
|
||||
OPTION_RFPOWER,
|
||||
OPTION_WBUS,
|
||||
};
|
||||
|
||||
const mm_protocol_definition multi_protocols[] = {
|
||||
// Protocol number, Protocol String, Sub_protocol strings, Number of sub_protocols, Option type, Failsafe, ChMap, RF switch, Init, Callback
|
||||
#if defined(MULTI_CONFIG_INO)
|
||||
{PROTO_CONFIG, STR_CONFIG, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, 0, CONFIG_init, CONFIG_callback },
|
||||
#endif
|
||||
#if defined(ASSAN_NRF24L01_INO)
|
||||
{PROTO_ASSAN, STR_ASSAN, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, ASSAN_init, ASSAN_callback },
|
||||
#endif
|
||||
#if defined(BAYANG_NRF24L01_INO)
|
||||
{PROTO_BAYANG, STR_BAYANG, STR_SUBTYPE_BAYANG, 6, OPTION_TELEM, 0, 0, SW_NRF, BAYANG_init, BAYANG_callback },
|
||||
#endif
|
||||
#if defined(BAYANG_RX_NRF24L01_INO)
|
||||
{PROTO_BAYANG_RX, STR_BAYANG_RX, STR_CPPM, NBR_CPPM, OPTION_NONE, 0, 0, SW_NRF, BAYANG_RX_init, BAYANG_RX_callback },
|
||||
#endif
|
||||
#if defined(BUGS_A7105_INO)
|
||||
{PROTO_BUGS, STR_BUGS, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_A7105, BUGS_init, BUGS_callback },
|
||||
#endif
|
||||
#if defined(BUGSMINI_NRF24L01_INO)
|
||||
{PROTO_BUGSMINI, STR_BUGSMINI, STR_SUBTYPE_BUGS_MINI, 2, OPTION_NONE, 0, 0, SW_NRF, BUGSMINI_init, BUGSMINI_callback },
|
||||
#endif
|
||||
#if defined(CABELL_NRF24L01_INO)
|
||||
{PROTO_CABELL, STR_CABELL, STR_SUBTYPE_CABELL, 8, OPTION_OPTION, 0, 0, SW_NRF, CABELL_init, CABELL_callback },
|
||||
#endif
|
||||
#if defined(CFLIE_NRF24L01_INO)
|
||||
{PROTO_CFLIE, STR_CFLIE, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, CFLIE_init, CFLIE_callback }, // review protocol
|
||||
#endif
|
||||
#if defined(CG023_NRF24L01_INO)
|
||||
{PROTO_CG023, STR_CG023, STR_SUBTYPE_CG023, 2, OPTION_NONE, 0, 0, SW_NRF, CG023_init, CG023_callback },
|
||||
#endif
|
||||
#if defined(CORONA_CC2500_INO)
|
||||
{PROTO_CORONA, STR_CORONA, STR_SUBTYPE_CORONA, 3, OPTION_RFTUNE, 0, 0, SW_CC2500, CORONA_init, CORONA_callback },
|
||||
#endif
|
||||
#if defined(CX10_NRF24L01_INO)
|
||||
{PROTO_CX10, STR_CX10, STR_SUBTYPE_CX10, 7, OPTION_NONE, 0, 0, SW_NRF, CX10_init, CX10_callback },
|
||||
#endif
|
||||
#if defined(DEVO_CYRF6936_INO)
|
||||
{PROTO_DEVO, STR_DEVO, STR_SUBTYPE_DEVO, 5, OPTION_FIXEDID, 1, 1, SW_CYRF, DEVO_init, DEVO_callback },
|
||||
#endif
|
||||
#if defined(DM002_NRF24L01_INO)
|
||||
{PROTO_DM002, STR_DM002, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, DM002_init, DM002_callback },
|
||||
#endif
|
||||
#if defined(DSM_CYRF6936_INO)
|
||||
{PROTO_DSM, STR_DSM, STR_SUBTYPE_DSM, 5, OPTION_MAXTHR, 0, 1, SW_CYRF, DSM_init, DSM_callback },
|
||||
#endif
|
||||
#if defined(DSM_RX_CYRF6936_INO)
|
||||
{PROTO_DSM_RX, STR_DSM_RX, STR_CPPM, NBR_CPPM, OPTION_NONE, 0, 1, SW_CYRF, DSM_RX_init, DSM_RX_callback },
|
||||
#endif
|
||||
#if defined(E010R5_CYRF6936_INO)
|
||||
{PROTO_E010R5, STR_E010R5, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_CYRF, E010R5_init, E010R5_callback },
|
||||
#endif
|
||||
#if defined(E016HV2_CC2500_INO)
|
||||
{PROTO_E016HV2, STR_E016HV2, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_CC2500, E016HV2_init, E016HV2_callback },
|
||||
#endif
|
||||
#if defined(E01X_NRF24L01_INO)
|
||||
{PROTO_E01X, STR_E01X, STR_SUBTYPE_E01X, 3, OPTION_OPTION, 0, 0, SW_NRF, E01X_init, E01X_callback },
|
||||
#endif
|
||||
#if defined(E129_CYRF6936_INO)
|
||||
{PROTO_E129, STR_E129, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_CYRF, E129_init, E129_callback },
|
||||
#endif
|
||||
#if defined(ESKY_NRF24L01_INO)
|
||||
{PROTO_ESKY, STR_ESKY, STR_SUBTYPE_ESKY, 2, OPTION_NONE, 0, 1, SW_NRF, ESKY_init, ESKY_callback },
|
||||
#endif
|
||||
#if defined(ESKY150_NRF24L01_INO)
|
||||
{PROTO_ESKY150, STR_ESKY150, STR_SUBTYPE_ESKY150, 2, OPTION_NONE, 0, 0, SW_NRF, ESKY150_init, ESKY150_callback },
|
||||
#endif
|
||||
#if defined(ESKY150V2_CC2500_INO)
|
||||
{PROTO_ESKY150V2, STR_ESKY150V2, STR_SUBTYPE_ESKY150V2, 1, OPTION_RFTUNE, 0, 1, SW_CC2500, ESKY150V2_init, ESKY150V2_callback },
|
||||
#endif
|
||||
#if defined(FLYSKY_A7105_INO)
|
||||
{PROTO_FLYSKY, STR_FLYSKY, STR_SUBTYPE_FLYSKY, 5, OPTION_NONE, 0, 1, SW_A7105, FLYSKY_init, FLYSKY_callback },
|
||||
#endif
|
||||
#if defined(AFHDS2A_A7105_INO)
|
||||
{PROTO_AFHDS2A, STR_AFHDS2A, STR_SUBTYPE_AFHDS2A, 8, OPTION_SRVFREQ, 1, 1, SW_A7105, AFHDS2A_init, AFHDS2A_callback },
|
||||
#endif
|
||||
#if defined(AFHDS2A_RX_A7105_INO)
|
||||
{PROTO_AFHDS2A_RX, STR_AFHDS2A_RX,STR_CPPM, NBR_CPPM, OPTION_NONE, 0, 0, SW_A7105, AFHDS2A_RX_init, AFHDS2A_RX_callback },
|
||||
#endif
|
||||
#if defined(FQ777_NRF24L01_INO)
|
||||
{PROTO_FQ777, STR_FQ777, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, FQ777_init, FQ777_callback },
|
||||
#endif
|
||||
//OpenTX 2.3.x issue: DO NOT CHANGE ORDER below
|
||||
#if defined(FRSKY_RX_CC2500_INO)
|
||||
{PROTO_FRSKY_RX, STR_FRSKY_RX, STR_SUB_FRSKY_RX, FRCPPM, OPTION_RFTUNE, 0, 0, SW_CC2500, FRSKY_RX_init, FRSKY_RX_callback },
|
||||
#endif
|
||||
#if defined(FRSKYD_CC2500_INO)
|
||||
{PROTO_FRSKYD, STR_FRSKYD, STR_SUBTYPE_FRSKYD, 2, OPTION_RFTUNE, 0, 0, SW_CC2500, FRSKYD_init, FRSKYD_callback },
|
||||
#endif
|
||||
#if defined(FRSKYV_CC2500_INO)
|
||||
{PROTO_FRSKYV, STR_FRSKYV, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_CC2500, FRSKYV_init, FRSKYV_callback },
|
||||
#endif
|
||||
#if defined(FRSKYX_CC2500_INO)
|
||||
{PROTO_FRSKYX, STR_FRSKYX, STR_SUBTYPE_FRSKYX, 6, OPTION_RFTUNE, 1, 0, SW_CC2500, FRSKYX_init, FRSKYX_callback },
|
||||
{PROTO_FRSKYX2, STR_FRSKYX2, STR_SUBTYPE_FRSKYX, 6, OPTION_RFTUNE, 1, 0, SW_CC2500, FRSKYX_init, FRSKYX_callback },
|
||||
#endif
|
||||
//OpenTX 2.3.x issue: DO NOT CHANGE ORDER above
|
||||
#if defined(FRSKYL_CC2500_INO)
|
||||
{PROTO_FRSKYL, STR_FRSKYL, STR_SUBTYPE_FRSKYL, 2, OPTION_RFTUNE, 0, 0, SW_CC2500, FRSKYL_init, FRSKYL_callback },
|
||||
#endif
|
||||
#if defined(FRSKYR9_SX1276_INO)
|
||||
#if MULTI_5IN1_INTERNAL == T18
|
||||
{PROTO_FRSKY_R9, STR_FRSKYR9, STR_SUBTYPE_FRSKYR9, 8, OPTION_NONE, 1, 0, 0, FRSKYR9_init, FRSKYR9_callback },
|
||||
#else // DIY & T-Lite
|
||||
{PROTO_FRSKY_R9, STR_FRSKYR9, STR_SUBTYPE_FRSKYR9, 8, OPTION_RFPOWER, 1, 0, 0, FRSKYR9_init, FRSKYR9_callback },
|
||||
#endif
|
||||
#endif
|
||||
#if defined(FUTABA_CC2500_INO)
|
||||
{PROTO_FUTABA, STR_FUTABA, STR_SUBTYPE_FUTABA, 1, OPTION_RFTUNE, 1, 1, SW_CC2500, SFHSS_init, SFHSS_callback },
|
||||
#endif
|
||||
#if defined(FX816_NRF24L01_INO)
|
||||
{PROTO_FX816, STR_FX816, STR_SUBTYPE_FX816, 1, OPTION_NONE, 0, 0, SW_NRF, FX816_init, FX816_callback },
|
||||
#endif
|
||||
#if defined(FY326_NRF24L01_INO)
|
||||
{PROTO_FY326, STR_FY326, STR_SUBTYPE_FY326, 2, OPTION_NONE, 0, 0, SW_NRF, FY326_init, FY326_callback },
|
||||
#endif
|
||||
#if defined(GD00X_CCNRF_INO)
|
||||
{PROTO_GD00X, STR_GD00X, STR_SUBTYPE_GD00X, 2, OPTION_RFTUNE, 0, 0, SW_NRF, GD00X_init, GD00X_callback },
|
||||
#endif
|
||||
#if defined(GW008_NRF24L01_INO)
|
||||
{PROTO_GW008, STR_GW008, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, GW008_init, GW008_callback },
|
||||
#endif
|
||||
#if defined(H8_3D_NRF24L01_INO)
|
||||
{PROTO_H8_3D, STR_H8_3D, STR_SUBTYPE_H83D, 4, OPTION_NONE, 0, 0, SW_NRF, H8_3D_init, H8_3D_callback },
|
||||
#endif
|
||||
#if defined(HEIGHT_A7105_INO)
|
||||
{PROTO_HEIGHT, STR_HEIGHT, STR_SUBTYPE_HEIGHT, 2, OPTION_NONE, 0, 0, SW_A7105, HEIGHT_init, HEIGHT_callback },
|
||||
#endif
|
||||
#if defined(HISKY_NRF24L01_INO)
|
||||
{PROTO_HISKY, STR_HISKY, STR_SUBTYPE_HISKY, 2, OPTION_NONE, 1, 1, SW_NRF, HISKY_init, HISKY_callback },
|
||||
#endif
|
||||
#if defined(HITEC_CC2500_INO)
|
||||
{PROTO_HITEC, STR_HITEC, STR_SUBTYPE_HITEC, 3, OPTION_RFTUNE, 0, 0, SW_CC2500, HITEC_init, HITEC_callback },
|
||||
#endif
|
||||
#if defined(HONTAI_NRF24L01_INO)
|
||||
{PROTO_HONTAI, STR_HONTAI, STR_SUBTYPE_HONTAI, 4, OPTION_NONE, 0, 0, SW_NRF, HONTAI_init, HONTAI_callback },
|
||||
#endif
|
||||
#if defined(HOTT_CC2500_INO)
|
||||
{PROTO_HOTT, STR_HOTT, STR_SUBTYPE_HOTT, 2, OPTION_RFTUNE, 1, 0, SW_CC2500, HOTT_init, HOTT_callback },
|
||||
#endif
|
||||
#if defined(HUBSAN_A7105_INO)
|
||||
{PROTO_HUBSAN, STR_HUBSAN, STR_SUBTYPE_HUBSAN, 3, OPTION_VIDFREQ, 0, 0, SW_A7105, HUBSAN_init, HUBSAN_callback },
|
||||
#endif
|
||||
#if defined(J6PRO_CYRF6936_INO)
|
||||
{PROTO_J6PRO, STR_J6PRO, NO_SUBTYPE, 0, OPTION_NONE, 0, 1, SW_CYRF, J6PRO_init, J6PRO_callback },
|
||||
#endif
|
||||
#if defined(JJRC345_NRF24L01_INO)
|
||||
{PROTO_JJRC345, STR_JJRC345, STR_SUBTYPE_JJRC345, 2, OPTION_NONE, 0, 0, SW_NRF, JJRC345_init, JJRC345_callback },
|
||||
#endif
|
||||
#if defined(JOYSWAY_A7105_INO)
|
||||
{PROTO_JOYSWAY, STR_JOYSWAY, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_A7105, JOYSWAY_init, JOYSWAY_callback },
|
||||
#endif
|
||||
#if defined(KF606_CCNRF_INO)
|
||||
{PROTO_KF606, STR_KF606, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_NRF, KF606_init, KF606_callback },
|
||||
#endif
|
||||
#if defined(KN_NRF24L01_INO)
|
||||
{PROTO_KN, STR_KN, STR_SUBTYPE_KN, 2, OPTION_NONE, 0, 0, SW_NRF, KN_init, KN_callback },
|
||||
#endif
|
||||
#if defined(KYOSHO_A7105_INO)
|
||||
{PROTO_KYOSHO, STR_KYOSHO, STR_SUBTYPE_KYOSHO, 2, OPTION_NONE, 0, 1, SW_A7105, KYOSHO_init, KYOSHO_callback },
|
||||
#endif
|
||||
#if defined(LOLI_NRF24L01_INO)
|
||||
{PROTO_LOLI, STR_LOLI, NO_SUBTYPE, 0, OPTION_NONE, 1, 0, SW_NRF, LOLI_init, LOLI_callback },
|
||||
#endif
|
||||
#if defined(MJXQ_NRF24L01_INO)
|
||||
{PROTO_MJXQ, STR_MJXQ, STR_SUBTYPE_MJXQ, 7, OPTION_RFTUNE, 0, 0, SW_NRF, MJXQ_init, MJXQ_callback },
|
||||
#endif
|
||||
#if defined(MLINK_CYRF6936_INO)
|
||||
{PROTO_MLINK, STR_MLINK, NO_SUBTYPE, 0, OPTION_NONE, 1, 0, SW_CYRF, MLINK_init, MLINK_callback },
|
||||
#endif
|
||||
#if defined(MT99XX_NRF24L01_INO)
|
||||
{PROTO_MT99XX, STR_MT99XX, STR_SUBTYPE_MT99, 7, OPTION_NONE, 0, 0, SW_NRF, MT99XX_init, MT99XX_callback },
|
||||
#endif
|
||||
#if defined(NCC1701_NRF24L01_INO)
|
||||
{PROTO_NCC1701, STR_NCC1701, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, NCC_init, NCC_callback },
|
||||
#endif
|
||||
#if defined(OMP_CC2500_INO)
|
||||
{PROTO_OMP, STR_OMP, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_NRF, OMP_init, OMP_callback },
|
||||
#endif
|
||||
#if defined(PELIKAN_A7105_INO)
|
||||
{PROTO_PELIKAN, STR_PELIKAN, STR_SUBTYPE_PELIKAN, 2, OPTION_NONE, 0, 1, SW_A7105, PELIKAN_init, PELIKAN_callback },
|
||||
#endif
|
||||
#if defined(POTENSIC_NRF24L01_INO)
|
||||
{PROTO_POTENSIC, STR_POTENSIC, STR_SUBTYPE_POTENSIC, 1, OPTION_NONE, 0, 0, SW_NRF, POTENSIC_init, POTENSIC_callback },
|
||||
#endif
|
||||
#if defined(PROPEL_NRF24L01_INO)
|
||||
{PROTO_PROPEL, STR_PROPEL, STR_SUBTYPE_PROPEL, 1, OPTION_NONE, 0, 0, SW_NRF, PROPEL_init, PROPEL_callback },
|
||||
#endif
|
||||
#if defined(CX10_NRF24L01_INO)
|
||||
{PROTO_Q2X2, STR_Q2X2, STR_SUBTYPE_Q2X2, 3, OPTION_NONE, 0, 0, SW_NRF, CX10_init, CX10_callback },
|
||||
#endif
|
||||
#if defined(Q303_NRF24L01_INO)
|
||||
{PROTO_Q303, STR_Q303, STR_SUBTYPE_Q303, 4, OPTION_NONE, 0, 0, SW_NRF, Q303_init, Q303_callback },
|
||||
#endif
|
||||
#if defined(Q90C_CCNRF_INO)
|
||||
{PROTO_Q90C, STR_Q90C, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_NRF, Q90C_init, Q90C_callback },
|
||||
#endif
|
||||
#if defined(RLINK_CC2500_INO)
|
||||
{PROTO_RLINK, STR_RLINK, STR_SUBTYPE_RLINK, 3, OPTION_RFTUNE, 0, 0, SW_CC2500, RLINK_init, RLINK_callback },
|
||||
#endif
|
||||
#if defined(REALACC_NRF24L01_INO)
|
||||
{PROTO_REALACC, STR_REALACC, STR_SUBTYPE_REALACC, 1, OPTION_NONE, 0, 0, SW_NRF, REALACC_init, REALACC_callback },
|
||||
#endif
|
||||
#if defined(REDPINE_CC2500_INO)
|
||||
{PROTO_REDPINE, STR_REDPINE, STR_SUBTYPE_REDPINE, 2, OPTION_RFTUNE, 0, 0, SW_CC2500, REDPINE_init, REDPINE_callback },
|
||||
#endif
|
||||
#if defined(SCANNER_CC2500_INO)
|
||||
{PROTO_SCANNER, STR_SCANNER, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_CC2500, SCANNER_init, SCANNER_callback },
|
||||
#endif
|
||||
#if defined(SHENQI_NRF24L01_INO)
|
||||
{PROTO_SHENQI, STR_SHENQI, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, SHENQI_init, SHENQI_callback },
|
||||
#endif
|
||||
#if defined(SKYARTEC_CC2500_INO)
|
||||
{PROTO_SKYARTEC, STR_SKYARTEC, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 1, SW_CC2500, SKYARTEC_init, SKYARTEC_callback },
|
||||
#endif
|
||||
#if defined(SLT_CCNRF_INO)
|
||||
{PROTO_SLT, STR_SLT, STR_SUBTYPE_SLT, 5, OPTION_RFTUNE, 0, 1, SW_NRF, SLT_init, SLT_callback },
|
||||
#endif
|
||||
#if defined(SYMAX_NRF24L01_INO)
|
||||
{PROTO_SYMAX, STR_SYMAX, STR_SUBTYPE_SYMAX, 2, OPTION_NONE, 0, 0, SW_NRF, SYMAX_init, SYMAX_callback },
|
||||
#endif
|
||||
#if defined(TIGER_NRF24L01_INO)
|
||||
{PROTO_TIGER, STR_TIGER, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, TIGER_init, TIGER_callback },
|
||||
#endif
|
||||
#if defined(TRAXXAS_CYRF6936_INO)
|
||||
{PROTO_TRAXXAS, STR_TRAXXAS, STR_SUBTYPE_TRAXXAS, 1, OPTION_NONE, 0, 0, SW_CYRF, TRAXXAS_init, TRAXXAS_callback },
|
||||
#endif
|
||||
#if defined(V2X2_NRF24L01_INO)
|
||||
{PROTO_V2X2, STR_V2X2, STR_SUBTYPE_V2X2, 3, OPTION_NONE, 0, 0, SW_NRF, V2X2_init, V2X2_callback },
|
||||
#endif
|
||||
#if defined(V761_NRF24L01_INO)
|
||||
{PROTO_V761, STR_V761, STR_SUBTYPE_V761, 2, OPTION_NONE, 0, 0, SW_NRF, V761_init, V761_callback },
|
||||
#endif
|
||||
#if defined(V911S_CCNRF_INO)
|
||||
{PROTO_V911S, STR_V911S, STR_SUBTYPE_V911S, 2, OPTION_RFTUNE, 0, 0, SW_NRF, V911S_init, V911S_callback },
|
||||
#endif
|
||||
#if defined(WK2x01_CYRF6936_INO)
|
||||
{PROTO_WK2x01, STR_WK2x01, STR_SUBTYPE_WK2x01, 6, OPTION_NONE, 1, 1, SW_CYRF, WK_init, WK_callback },
|
||||
#endif
|
||||
#if defined(WFLY_CYRF6936_INO)
|
||||
{PROTO_WFLY, STR_WFLY, STR_SUBTYPE_WFLY, 1, OPTION_NONE, 1, 0, SW_CYRF, WFLY_init, WFLY_callback },
|
||||
#endif
|
||||
#if defined(WFLY2_A7105_INO)
|
||||
{PROTO_WFLY2, STR_WFLY2, STR_SUBTYPE_WFLY2, 1, OPTION_OPTION, 1, 0, SW_A7105, WFLY2_init, WFLY2_callback },
|
||||
// {PROTO_WFLY2, STR_WFLY2, STR_SUBTYPE_WFLY2, 1, OPTION_WBUS, 1, 0, SW_A7105, WFLY2_init, WFLY2_callback },// crash OpenTX...
|
||||
#endif
|
||||
#if defined(XK_NRF24L01_INO)
|
||||
{PROTO_XK, STR_XK, STR_SUBTYPE_XK, 2, OPTION_RFTUNE, 0, 0, SW_NRF, XK_init, XK_callback },
|
||||
#endif
|
||||
#if defined(XN297DUMP_NRF24L01_INO)
|
||||
{PROTO_XN297DUMP, STR_XN297DUMP, STR_SUBTYPE_XN297DUMP, 5, OPTION_RFCHAN, 0, 0, SW_NRF, XN297Dump_init, XN297Dump_callback },
|
||||
#endif
|
||||
#if defined(YD717_NRF24L01_INO)
|
||||
{PROTO_YD717, STR_YD717, STR_SUBTYPE_YD717, 5, OPTION_NONE, 0, 0, SW_NRF, YD717_init, YD717_callback },
|
||||
#endif
|
||||
#if defined(ZSX_NRF24L01_INO)
|
||||
{PROTO_ZSX, STR_ZSX, STR_SUBTYPE_ZSX, 1, OPTION_NONE, 0, 0, SW_NRF, ZSX_init, ZSX_callback },
|
||||
#endif
|
||||
#if defined(TEST_CC2500_INO)
|
||||
{PROTO_TEST, STR_TEST, NO_SUBTYPE, 0, OPTION_RFTUNE, 0, 0, SW_NRF, TEST_init, TEST_callback },
|
||||
#endif
|
||||
#if defined(NANORF_NRF24L01_INO)
|
||||
{PROTO_NANORF, STR_NANORF, NO_SUBTYPE, 0, OPTION_NONE, 0, 0, SW_NRF, NANORF_init, NANORF_callback },
|
||||
#endif
|
||||
{0x00, nullptr, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr }
|
||||
};
|
||||
1254
Multiprotocol/Multiprotocol.h
Normal file
1254
Multiprotocol/Multiprotocol.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
267
Multiprotocol/NCC1701_nrf24l01.ino
Normal file
267
Multiprotocol/NCC1701_nrf24l01.ino
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
This project is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Multiprotocol is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(NCC1701_NRF24L01_INO)
|
||||
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
#define NCC_WRITE_WAIT 2000
|
||||
#define NCC_PACKET_INTERVAL 10333
|
||||
#define NCC_TX_PACKET_LEN 16
|
||||
#define NCC_RX_PACKET_LEN 13
|
||||
|
||||
enum {
|
||||
NCC_BIND_TX1=0,
|
||||
NCC_BIND_RX1,
|
||||
NCC_BIND_TX2,
|
||||
NCC_BIND_RX2,
|
||||
NCC_TX3,
|
||||
NCC_RX3,
|
||||
};
|
||||
|
||||
static void __attribute__((unused)) NCC_RF_init()
|
||||
{
|
||||
NRF24L01_Initialize();
|
||||
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, (uint8_t*)"\xE7\xE7\xC7\xD7\x67",5);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\xE7\xE7\xC7\xD7\x67",5);
|
||||
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, NCC_RX_PACKET_LEN); // Enable rx pipe 0
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_250K); // NRF24L01_BR_1M, NRF24L01_BR_2M, NRF24L01_BR_250K
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (0 << NRF24L01_00_EN_CRC) // switch to TX mode and disable CRC
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (0 << NRF24L01_00_PRIM_RX));
|
||||
}
|
||||
|
||||
const uint8_t NCC_xor[]={0x80, 0x44, 0x64, 0x75, 0x6C, 0x71, 0x2A, 0x36, 0x7C, 0xF1, 0x6E, 0x52, 0x09, 0x9D};
|
||||
static void __attribute__((unused)) NCC_Crypt_Packet()
|
||||
{
|
||||
crc=0;
|
||||
for(uint8_t i=0; i< NCC_TX_PACKET_LEN-2; i++)
|
||||
{
|
||||
packet[i]^=NCC_xor[i];
|
||||
crc16_update(packet[i], 8);
|
||||
}
|
||||
crc^=0x60DE;
|
||||
packet[NCC_TX_PACKET_LEN-2]=crc>>8;
|
||||
packet[NCC_TX_PACKET_LEN-1]=crc;
|
||||
}
|
||||
static boolean __attribute__((unused)) NCC_Decrypt_Packet()
|
||||
{
|
||||
crc=0;
|
||||
debug("RX: ");
|
||||
for(uint8_t i=0; i< NCC_RX_PACKET_LEN-2; i++)
|
||||
{
|
||||
crc16_update( packet[i], 8);
|
||||
packet[i]^=NCC_xor[i];
|
||||
debug("%02X ",packet[i]);
|
||||
}
|
||||
crc^=0xA950;
|
||||
if( (crc>>8)==packet[NCC_RX_PACKET_LEN-2] && (crc&0xFF)==packet[NCC_RX_PACKET_LEN-1] )
|
||||
{// CRC match
|
||||
debugln("OK");
|
||||
return true;
|
||||
}
|
||||
debugln("NOK");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) NCC_Write_Packet()
|
||||
{
|
||||
packet[0]=0xAA;
|
||||
packet[1]=rx_tx_addr[0];
|
||||
packet[2]=rx_tx_addr[1];
|
||||
packet[3]=rx_id[0];
|
||||
packet[4]=rx_id[1];
|
||||
packet[5]=convert_channel_8b(THROTTLE)>>2; // 00-3D
|
||||
packet[6]=convert_channel_8b(ELEVATOR); // original: 61-80-9F but works with 00-80-FF
|
||||
packet[7]=convert_channel_8b(AILERON ); // original: 61-80-9F but works with 00-80-FF
|
||||
packet[8]=convert_channel_8b(RUDDER ); // original: 61-80-9F but works with 00-80-FF
|
||||
packet[9]=rx_id[2];
|
||||
packet[10]=rx_id[3];
|
||||
packet[11]=rx_id[4];
|
||||
packet[12]=GET_FLAG(CH5_SW, 0x02); // Warp:0x00 -> 0x02
|
||||
packet[13]=packet[5]+packet[6]+packet[7]+packet[8]+packet[12];
|
||||
if(phase==NCC_BIND_TX1)
|
||||
{
|
||||
packet[0]=0xBB;
|
||||
packet[5]=0x01;
|
||||
packet[6]=rx_tx_addr[2];
|
||||
memset((void *)(packet+7),0x55,7);
|
||||
hopping_frequency_no^=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hopping_frequency_no++;
|
||||
if(hopping_frequency_no>2) hopping_frequency_no=0;
|
||||
}
|
||||
// change frequency
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
|
||||
// switch to TX mode and disable CRC
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (0 << NRF24L01_00_EN_CRC)
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (0 << NRF24L01_00_PRIM_RX));
|
||||
// clear packet status bits and TX FIFO
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
|
||||
NRF24L01_FlushTx();
|
||||
// send packet
|
||||
NCC_Crypt_Packet();
|
||||
NRF24L01_WritePayload(packet,NCC_TX_PACKET_LEN);
|
||||
NRF24L01_SetPower();
|
||||
}
|
||||
|
||||
uint16_t NCC_callback()
|
||||
{
|
||||
switch(phase)
|
||||
{
|
||||
case NCC_BIND_TX1:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
NRF24L01_ReadPayload(packet, NCC_RX_PACKET_LEN);
|
||||
if(NCC_Decrypt_Packet() && packet[1]==rx_tx_addr[0] && packet[2]==rx_tx_addr[1])
|
||||
|
||||
{
|
||||
rx_id[0]=packet[3];
|
||||
rx_id[1]=packet[4];
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
phase=NCC_BIND_TX2;
|
||||
return NCC_PACKET_INTERVAL;
|
||||
}
|
||||
}
|
||||
NCC_Write_Packet();
|
||||
phase = NCC_BIND_RX1;
|
||||
return NCC_WRITE_WAIT;
|
||||
case NCC_BIND_RX1:
|
||||
// switch to RX mode and disable CRC
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (0 << NRF24L01_00_EN_CRC)
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (1 << NRF24L01_00_PRIM_RX));
|
||||
NRF24L01_FlushRx();
|
||||
phase = NCC_BIND_TX1;
|
||||
return NCC_PACKET_INTERVAL - NCC_WRITE_WAIT;
|
||||
case NCC_BIND_TX2:
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
NRF24L01_ReadPayload(packet, NCC_RX_PACKET_LEN);
|
||||
if(NCC_Decrypt_Packet() && packet[1]==rx_tx_addr[0] && packet[2]==rx_tx_addr[1] && packet[3]==rx_id[0] && packet[4]==rx_id[1])
|
||||
{
|
||||
rx_id[2]=packet[8];
|
||||
rx_id[3]=packet[9];
|
||||
rx_id[4]=packet[10];
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // Clear data ready, data sent, and retransmit
|
||||
BIND_DONE;
|
||||
phase=NCC_TX3;
|
||||
return NCC_PACKET_INTERVAL;
|
||||
}
|
||||
}
|
||||
NCC_Write_Packet();
|
||||
phase = NCC_BIND_RX2;
|
||||
return NCC_WRITE_WAIT;
|
||||
case NCC_BIND_RX2:
|
||||
// switch to RX mode and disable CRC
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (0 << NRF24L01_00_EN_CRC)
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (1 << NRF24L01_00_PRIM_RX));
|
||||
NRF24L01_FlushRx();
|
||||
phase = NCC_BIND_TX2;
|
||||
return NCC_PACKET_INTERVAL - NCC_WRITE_WAIT;
|
||||
case NCC_TX3:
|
||||
#ifdef MULTI_SYNC
|
||||
telemetry_set_input_sync(NCC_PACKET_INTERVAL);
|
||||
#endif
|
||||
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
|
||||
{ // RX fifo data ready
|
||||
NRF24L01_ReadPayload(packet, NCC_RX_PACKET_LEN);
|
||||
if(NCC_Decrypt_Packet() && packet[1]==rx_tx_addr[0] && packet[2]==rx_tx_addr[1] && packet[3]==rx_id[0] && packet[4]==rx_id[1])
|
||||
{
|
||||
//Telemetry
|
||||
//packet[5] and packet[7] roll angle
|
||||
//packet[6] crash detect: 0x00 no crash, 0x02 crash
|
||||
#ifdef NCC1701_HUB_TELEMETRY
|
||||
v_lipo1 = packet[6]?0xFF:0x00; // Crash indication
|
||||
v_lipo2 = 0x00;
|
||||
RX_RSSI = 0x7F; // Dummy RSSI
|
||||
TX_RSSI = 0x7F; // Dummy RSSI
|
||||
telemetry_link=1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
NCC_Write_Packet();
|
||||
phase = NCC_RX3;
|
||||
return NCC_WRITE_WAIT;
|
||||
case NCC_RX3:
|
||||
// switch to RX mode and disable CRC
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (0 << NRF24L01_00_EN_CRC)
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (1 << NRF24L01_00_PRIM_RX));
|
||||
NRF24L01_FlushRx();
|
||||
phase = NCC_TX3;
|
||||
return NCC_PACKET_INTERVAL - NCC_WRITE_WAIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t PROGMEM NCC_TX_DATA[][6]= {
|
||||
{ 0x6D, 0x97, 0x04, 0x48, 0x43, 0x26 },
|
||||
{ 0x35, 0x4B, 0x80, 0x44, 0x4C, 0x0B },
|
||||
{ 0x50, 0xE2, 0x32, 0x2D, 0x4B, 0x0A },
|
||||
{ 0xBF, 0x34, 0xF3, 0x45, 0x4D, 0x0D },
|
||||
{ 0xDD, 0x7D, 0x5A, 0x46, 0x28, 0x23 },
|
||||
{ 0xED, 0x19, 0x06, 0x2C, 0x4A, 0x09 },
|
||||
{ 0xE9, 0xA8, 0x91, 0x2B, 0x49, 0x07 },
|
||||
{ 0x66, 0x17, 0x7D, 0x48, 0x43, 0x26 },
|
||||
{ 0xC2, 0x93, 0x55, 0x44, 0x4C, 0x0B },
|
||||
};
|
||||
|
||||
void NCC_init(void)
|
||||
{
|
||||
BIND_IN_PROGRESS; // autobind protocol
|
||||
|
||||
// Load TX data
|
||||
uint8_t rand=rx_tx_addr[3]%9;
|
||||
for(uint8_t i=0; i<3; i++)
|
||||
{
|
||||
rx_tx_addr[i]=pgm_read_byte_near(&NCC_TX_DATA[rand][i]);
|
||||
hopping_frequency[i]=pgm_read_byte_near(&NCC_TX_DATA[rand][i+3]);
|
||||
}
|
||||
|
||||
// RX data is acquired during bind
|
||||
rx_id[0]=0x00;
|
||||
rx_id[1]=0x00;
|
||||
rx_id[2]=0x20;
|
||||
rx_id[3]=0x20;
|
||||
rx_id[4]=0x20;
|
||||
|
||||
hopping_frequency[4]=0x08; // bind channel 1
|
||||
hopping_frequency[5]=0x2A; // bind channel 2
|
||||
hopping_frequency_no=4; // start with bind
|
||||
NCC_RF_init();
|
||||
phase=NCC_BIND_TX1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -14,50 +14,9 @@
|
||||
*/
|
||||
|
||||
|
||||
//---------------------------
|
||||
// AVR nrf chip bitbang SPI functions
|
||||
//---------------------------
|
||||
#ifdef NRF24L01_INSTALLED
|
||||
#include "iface_nrf24l01.h"
|
||||
|
||||
void nrf_spi_write(uint8_t command)
|
||||
{
|
||||
uint8_t n=8;
|
||||
|
||||
SCK_off;//SCK start low
|
||||
SDI_off;
|
||||
while(n--) {
|
||||
if(command&0x80)
|
||||
SDI_on;
|
||||
else
|
||||
SDI_off;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
command = command << 1;
|
||||
}
|
||||
SDI_on;
|
||||
}
|
||||
|
||||
//VARIANT 2
|
||||
uint8_t nrf_spi_read(void)
|
||||
{
|
||||
uint8_t result;
|
||||
uint8_t i;
|
||||
result=0;
|
||||
for(i=0;i<8;i++) {
|
||||
result<<=1;
|
||||
if(SDO_1) ///
|
||||
result|=0x01;
|
||||
SCK_on;
|
||||
NOP();
|
||||
SCK_off;
|
||||
NOP();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------
|
||||
|
||||
|
||||
#include "iface_xn297.h"
|
||||
|
||||
//---------------------------
|
||||
// NRF24L01+ SPI Specific Functions
|
||||
@@ -68,13 +27,28 @@ uint8_t rf_setup;
|
||||
void NRF24L01_Initialize()
|
||||
{
|
||||
rf_setup = 0x09;
|
||||
prev_power = 0x00; // Make sure prev_power is inline with current power
|
||||
XN297_SetScrambledMode(XN297_SCRAMBLED);
|
||||
|
||||
//Load most likely default NRF config
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_WriteReg(NRF24L01_01_EN_AA, 0x00); // No Auto Acknowldgement on all data pipes
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, 0x03); // 5 bytes rx/tx address
|
||||
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0x00); // no retransmits
|
||||
NRF24L01_SetBitrate(NRF24L01_BR_1M); // 1Mbps
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x01); // Set feature bits off and enable the command NRF24L01_B0_TX_PYLD_NOACK
|
||||
NRF24L01_SetPower();
|
||||
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
|
||||
}
|
||||
|
||||
void NRF24L01_WriteReg(uint8_t reg, uint8_t data)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(W_REGISTER | (REGISTER_MASK & reg));
|
||||
nrf_spi_write(data);
|
||||
SPI_Write(W_REGISTER | (REGISTER_MASK & reg));
|
||||
SPI_Write(data);
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
@@ -82,52 +56,53 @@ void NRF24L01_WriteRegisterMulti(uint8_t reg, uint8_t * data, uint8_t length)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
|
||||
nrf_spi_write(W_REGISTER | ( REGISTER_MASK & reg));
|
||||
SPI_Write(W_REGISTER | ( REGISTER_MASK & reg));
|
||||
for (uint8_t i = 0; i < length; i++)
|
||||
nrf_spi_write(data[i]);
|
||||
SPI_Write(data[i]);
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
void NRF24L01_WritePayload(uint8_t * data, uint8_t length)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(W_TX_PAYLOAD);
|
||||
SPI_Write(W_TX_PAYLOAD);
|
||||
for (uint8_t i = 0; i < length; i++)
|
||||
nrf_spi_write(data[i]);
|
||||
SPI_Write(data[i]);
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
uint8_t NRF24L01_ReadReg(uint8_t reg)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(R_REGISTER | (REGISTER_MASK & reg));
|
||||
uint8_t data = nrf_spi_read();
|
||||
SPI_Write(R_REGISTER | (REGISTER_MASK & reg));
|
||||
uint8_t data = SPI_Read();
|
||||
NRF_CSN_on;
|
||||
return data;
|
||||
}
|
||||
|
||||
void NRF24L01_ReadRegisterMulti(uint8_t reg, uint8_t * data, uint8_t length)
|
||||
/*static void NRF24L01_ReadRegisterMulti(uint8_t reg, uint8_t * data, uint8_t length)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(R_REGISTER | (REGISTER_MASK & reg));
|
||||
SPI_Write(R_REGISTER | (REGISTER_MASK & reg));
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
data[i] = nrf_spi_read();
|
||||
data[i] = SPI_Read();
|
||||
NRF_CSN_on;
|
||||
}
|
||||
*/
|
||||
|
||||
static void NRF24L01_ReadPayload(uint8_t * data, uint8_t length)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
SPI_Write(R_RX_PAYLOAD);
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
data[i] = SPI_Read();
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
void NRF24L01_ReadPayload(uint8_t * data, uint8_t length)
|
||||
static void NRF24L01_Strobe(uint8_t state)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(R_RX_PAYLOAD);
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
data[i] = nrf_spi_read();
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
void NRF24L01_Strobe(uint8_t state)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(state);
|
||||
SPI_Write(state);
|
||||
NRF_CSN_on;
|
||||
}
|
||||
|
||||
@@ -141,27 +116,43 @@ void NRF24L01_FlushRx()
|
||||
NRF24L01_Strobe(FLUSH_RX);
|
||||
}
|
||||
|
||||
void NRF24L01_Activate(uint8_t code)
|
||||
static uint8_t __attribute__((unused)) NRF24L01_GetStatus()
|
||||
{
|
||||
return SPI_Read();
|
||||
}
|
||||
|
||||
static uint8_t NRF24L01_GetDynamicPayloadSize()
|
||||
{
|
||||
NRF_CSN_off;
|
||||
nrf_spi_write(ACTIVATE);
|
||||
nrf_spi_write(code);
|
||||
SPI_Write(R_RX_PL_WID);
|
||||
uint8_t len = SPI_Read();
|
||||
NRF_CSN_on;
|
||||
return len;
|
||||
}
|
||||
|
||||
/*void NRF24L01_Activate(uint8_t code)
|
||||
{
|
||||
NRF_CSN_off;
|
||||
SPI_Write(ACTIVATE);
|
||||
SPI_Write(code);
|
||||
NRF_CSN_on;
|
||||
}*/
|
||||
|
||||
void NRF24L01_SetBitrate(uint8_t bitrate)
|
||||
{
|
||||
// Note that bitrate 250kbps (and bit RF_DR_LOW) is valid only
|
||||
// for nRF24L01+. There is no way to programmatically tell it from
|
||||
// older version, nRF24L01, but the older is practically phased out
|
||||
// by Nordic, so we assume that we deal with with modern version.
|
||||
// by Nordic, so we assume that we deal with modern version.
|
||||
|
||||
// Bit 0 goes to RF_DR_HIGH, bit 1 - to RF_DR_LOW
|
||||
rf_setup = (rf_setup & 0xD7) | ((bitrate & 0x02) << 4) | ((bitrate & 0x01) << 3);
|
||||
prev_power=(rf_setup>>1)&0x03; // Make sure prev_power is inline with current power
|
||||
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
|
||||
}
|
||||
|
||||
void NRF24L01_SetPower_Value(uint8_t power)
|
||||
/*
|
||||
static void NRF24L01_SetPower_Value(uint8_t power)
|
||||
{
|
||||
uint8_t nrf_power = 0;
|
||||
switch(power) {
|
||||
@@ -179,36 +170,47 @@ void NRF24L01_SetPower_Value(uint8_t power)
|
||||
rf_setup = (rf_setup & 0xF9) | ((nrf_power & 0x03) << 1);
|
||||
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
|
||||
}
|
||||
|
||||
*/
|
||||
void NRF24L01_SetPower()
|
||||
{
|
||||
uint8_t power=NRF_BIND_POWER;
|
||||
if(IS_BIND_DONE_on)
|
||||
if(IS_BIND_DONE)
|
||||
#ifdef NRF24L01_ENABLE_LOW_POWER
|
||||
power=IS_POWER_FLAG_on?NRF_HIGH_POWER:NRF_LOW_POWER;
|
||||
#else
|
||||
power=NRF_HIGH_POWER;
|
||||
#endif
|
||||
if(IS_RANGE_FLAG_on)
|
||||
power=NRF_POWER_0;
|
||||
if(prev_power != power)
|
||||
{
|
||||
rf_setup = (rf_setup & 0xF9) | (power << 1);
|
||||
if(power==3)
|
||||
rf_setup |=0x01; // Si24r01 full power, unused bit for NRF
|
||||
else
|
||||
rf_setup &=0xFE;
|
||||
NRF24L01_WriteReg(NRF24L01_06_RF_SETUP, rf_setup);
|
||||
prev_power=power;
|
||||
}
|
||||
}
|
||||
|
||||
void NRF24L01_SetTxRxMode(enum TXRX_State mode)
|
||||
{
|
||||
if(mode == TX_EN) {
|
||||
NRF_CSN_off;
|
||||
NRF_CE_off;
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, (1 << NRF24L01_07_RX_DR) //reset the flag(s)
|
||||
| (1 << NRF24L01_07_TX_DS)
|
||||
| (1 << NRF24L01_07_MAX_RT));
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_EN_CRC) // switch to TX mode
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP));
|
||||
_delay_us(130);
|
||||
NRF_CSN_on;
|
||||
delayMicroseconds(130);
|
||||
NRF_CE_on;
|
||||
}
|
||||
else
|
||||
if (mode == RX_EN) {
|
||||
NRF_CSN_off;
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70); // reset the flag(s)
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x0F); // switch to RX mode
|
||||
if (mode == RX_EN)
|
||||
{
|
||||
NRF_CE_off;
|
||||
NRF24L01_WriteReg(NRF24L01_07_STATUS, (1 << NRF24L01_07_RX_DR) //reset the flag(s)
|
||||
| (1 << NRF24L01_07_TX_DS)
|
||||
| (1 << NRF24L01_07_MAX_RT));
|
||||
@@ -216,202 +218,337 @@ void NRF24L01_SetTxRxMode(enum TXRX_State mode)
|
||||
| (1 << NRF24L01_00_CRCO)
|
||||
| (1 << NRF24L01_00_PWR_UP)
|
||||
| (1 << NRF24L01_00_PRIM_RX));
|
||||
_delay_us(130);
|
||||
NRF_CSN_on;
|
||||
delayMicroseconds(130);
|
||||
NRF_CE_on;
|
||||
}
|
||||
else
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_EN_CRC)); //PowerDown
|
||||
NRF_CSN_off;
|
||||
NRF_CE_off;
|
||||
}
|
||||
}
|
||||
|
||||
void NRF24L01_Reset()
|
||||
{
|
||||
//** not in deviation but needed to hot switch between models
|
||||
NRF24L01_Activate(0x73); // Activate feature register
|
||||
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x00); // Disable dynamic payload length on all pipes
|
||||
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x00); // Set feature bits off
|
||||
NRF24L01_Activate(0x73);
|
||||
//**
|
||||
|
||||
NRF24L01_FlushTx();
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_Strobe(0xff); // NOP
|
||||
NRF24L01_ReadReg(0x07);
|
||||
NRF24L01_ReadReg(NRF24L01_07_STATUS);
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
_delay_us(100);
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
|
||||
uint8_t NRF24L01_packet_ack()
|
||||
{
|
||||
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (BV(NRF24L01_07_TX_DS) | BV(NRF24L01_07_MAX_RT))) {
|
||||
case BV(NRF24L01_07_TX_DS):
|
||||
switch (NRF24L01_ReadReg(NRF24L01_07_STATUS) & (_BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT)))
|
||||
{
|
||||
case _BV(NRF24L01_07_TX_DS):
|
||||
return PKT_ACKED;
|
||||
case BV(NRF24L01_07_MAX_RT):
|
||||
case _BV(NRF24L01_07_MAX_RT):
|
||||
return PKT_TIMEOUT;
|
||||
}
|
||||
return PKT_PENDING;
|
||||
}
|
||||
|
||||
//
|
||||
// HS6200 emulation layer
|
||||
///////////////////////////
|
||||
static uint8_t hs6200_crc;
|
||||
static uint16_t hs6200_crc_init;
|
||||
static uint8_t hs6200_tx_addr[5];
|
||||
static uint8_t hs6200_address_length;
|
||||
|
||||
static const uint8_t hs6200_scramble[] = {
|
||||
0x80,0xf5,0x3b,0x0d,0x6d,0x2a,0xf9,0xbc,
|
||||
0x51,0x8e,0x4c,0xfd,0xc1,0x65,0xd0 }; // todo: find all 32 bytes ...
|
||||
|
||||
//---------------------------
|
||||
/*
|
||||
void NRF24L01_spi_test(void)
|
||||
void HS6200_SetTXAddr(const uint8_t* addr, uint8_t len)
|
||||
{
|
||||
unsigned long errors = 0;
|
||||
unsigned long test = 0;
|
||||
unsigned long time;
|
||||
uint8_t test_data_r[5];
|
||||
uint8_t test_data_w[5] = {0x01,0x02,0x03,0x04,0x05};
|
||||
if(len < 4)
|
||||
len = 4;
|
||||
else if(len > 5)
|
||||
len = 5;
|
||||
|
||||
time = micros();
|
||||
Serial.println("Testing SPI");
|
||||
for(test=0; test < 2775600 ; test++) // should run for X mins.
|
||||
{
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0B_RX_ADDR_P1, test_data_w, 5);
|
||||
NRF24L01_ReadRegisterMulti(NRF24L01_0B_RX_ADDR_P1, test_data_r, 5);
|
||||
if(0 != memcmp(test_data_r, test_data_w, sizeof(test_data_r))) errors++;
|
||||
test_data_w[0] ++;
|
||||
test_data_w[1] ++;
|
||||
test_data_w[2] ++;
|
||||
test_data_w[3] ++;
|
||||
test_data_w[4] ++;
|
||||
}
|
||||
Serial.print("test "); Serial.print(test, HEX); Serial.print("\n");
|
||||
Serial.print("errors "); Serial.print(errors, HEX); Serial.print("\n");
|
||||
Serial.print("time "); Serial.print(micros()- time, DEC); Serial.print("\n");
|
||||
|
||||
// 124211960
|
||||
// 90899216
|
||||
}
|
||||
*/
|
||||
//---------------------------
|
||||
|
||||
|
||||
///////////////
|
||||
// XN297 emulation layer
|
||||
uint8_t xn297_addr_len;
|
||||
uint8_t xn297_tx_addr[5];
|
||||
uint8_t xn297_rx_addr[5];
|
||||
uint8_t xn297_crc = 0;
|
||||
|
||||
static const uint8_t xn297_scramble[] = {
|
||||
0xe3, 0xb1, 0x4b, 0xea, 0x85, 0xbc, 0xe5, 0x66,
|
||||
0x0d, 0xae, 0x8c, 0x88, 0x12, 0x69, 0xee, 0x1f,
|
||||
0xc7, 0x62, 0x97, 0xd5, 0x0b, 0x79, 0xca, 0xcc,
|
||||
0x1b, 0x5d, 0x19, 0x10, 0x24, 0xd3, 0xdc, 0x3f,
|
||||
0x8e, 0xc5, 0x2f};
|
||||
|
||||
static const uint16_t xn297_crc_xorout[] = {
|
||||
0x0000, 0x3448, 0x9BA7, 0x8BBB, 0x85E1, 0x3E8C, // 1st entry is missing, probably never needed
|
||||
0x451E, 0x18E6, 0x6B24, 0xE7AB, 0x3828, 0x8148, // it's used for 3-byte address w/ 0 byte payload only
|
||||
0xD461, 0xF494, 0x2503, 0x691D, 0xFE8B, 0x9BA7,
|
||||
0x8B17, 0x2920, 0x8B5F, 0x61B1, 0xD391, 0x7401,
|
||||
0x2138, 0x129F, 0xB3A0, 0x2988};
|
||||
|
||||
uint8_t bit_reverse(uint8_t b_in)
|
||||
{
|
||||
uint8_t b_out = 0;
|
||||
for (uint8_t i = 0; i < 8; ++i)
|
||||
{
|
||||
b_out = (b_out << 1) | (b_in & 1);
|
||||
b_in >>= 1;
|
||||
}
|
||||
return b_out;
|
||||
}
|
||||
|
||||
uint16_t crc16_update(uint16_t crc, uint8_t a)
|
||||
{
|
||||
static const uint16_t polynomial = 0x1021;
|
||||
|
||||
crc ^= a << 8;
|
||||
for (uint8_t i = 0; i < 8; ++i)
|
||||
if (crc & 0x8000)
|
||||
crc = (crc << 1) ^ polynomial;
|
||||
// use nrf24 address field as a longer preamble
|
||||
if(addr[len-1] & 0x80)
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\x55\x55\x55\x55\x55", 5);
|
||||
else
|
||||
crc = crc << 1;
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\xaa\xaa\xaa\xaa\xaa", 5);
|
||||
|
||||
// precompute address crc
|
||||
crc = 0xffff;
|
||||
for(int i=0; i<len; i++)
|
||||
crc16_update(addr[len-1-i], 8);
|
||||
hs6200_crc_init=crc;
|
||||
memcpy(hs6200_tx_addr, addr, len);
|
||||
hs6200_address_length = len;
|
||||
}
|
||||
|
||||
static uint16_t hs6200_calc_crc(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
uint8_t pos;
|
||||
|
||||
crc = hs6200_crc_init;
|
||||
// pcf + payload
|
||||
for(pos=0; pos < len-1; pos++)
|
||||
crc16_update(msg[pos], 8);
|
||||
// last byte (1 bit only)
|
||||
if(len > 0)
|
||||
crc16_update(msg[pos+1], 1);
|
||||
return crc;
|
||||
}
|
||||
|
||||
void XN297_SetTXAddr(const uint8_t* addr, uint8_t len)
|
||||
void HS6200_Configure(uint8_t flags)
|
||||
{
|
||||
if (len > 5) len = 5;
|
||||
if (len < 3) len = 3;
|
||||
uint8_t buf[] = { 0x55, 0x0F, 0x71, 0x0C, 0x00 }; // bytes for XN297 preamble 0xC710F55 (28 bit)
|
||||
xn297_addr_len = len;
|
||||
if (xn297_addr_len < 4)
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
buf[i] = buf[i+1];
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, buf, 5);
|
||||
// Receive address is complicated. We need to use scrambled actual address as a receive address
|
||||
// but the TX code now assumes fixed 4-byte transmit address for preamble. We need to adjust it
|
||||
// first. Also, if the scrambled address begins with 1 nRF24 will look for preamble byte 0xAA
|
||||
// instead of 0x55 to ensure enough 0-1 transitions to tune the receiver. Still need to experiment
|
||||
// with receiving signals.
|
||||
memcpy(xn297_tx_addr, addr, len);
|
||||
hs6200_crc = !!(flags & _BV(NRF24L01_00_EN_CRC));
|
||||
flags &= ~(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO));
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags & 0xff);
|
||||
}
|
||||
|
||||
void XN297_SetRXAddr(const uint8_t* addr, uint8_t len)
|
||||
void HS6200_WritePayload(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
if (len > 5) len = 5;
|
||||
if (len < 3) len = 3;
|
||||
uint8_t buf[] = { 0, 0, 0, 0, 0 };
|
||||
memcpy(buf, addr, len);
|
||||
memcpy(xn297_rx_addr, addr, len);
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
buf[i] = xn297_rx_addr[i] ^ xn297_scramble[xn297_addr_len-i-1];
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, len-2);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, buf, 5);
|
||||
uint8_t payload[32];
|
||||
const uint8_t no_ack = 1; // never ask for an ack
|
||||
static uint8_t pid;
|
||||
uint8_t pos = 0;
|
||||
|
||||
if(len > sizeof(hs6200_scramble))
|
||||
len = sizeof(hs6200_scramble);
|
||||
|
||||
// address
|
||||
for(int i=hs6200_address_length-1; i>=0; i--)
|
||||
payload[pos++] = hs6200_tx_addr[i];
|
||||
|
||||
// guard bytes
|
||||
payload[pos++] = hs6200_tx_addr[0];
|
||||
payload[pos++] = hs6200_tx_addr[0];
|
||||
|
||||
// packet control field
|
||||
payload[pos++] = ((len & 0x3f) << 2) | (pid & 0x03);
|
||||
payload[pos] = (no_ack & 0x01) << 7;
|
||||
pid++;
|
||||
|
||||
// scrambled payload
|
||||
if(len > 0)
|
||||
{
|
||||
payload[pos++] |= (msg[0] ^ hs6200_scramble[0]) >> 1;
|
||||
for(uint8_t i=1; i<len; i++)
|
||||
payload[pos++] = ((msg[i-1] ^ hs6200_scramble[i-1]) << 7) | ((msg[i] ^ hs6200_scramble[i]) >> 1);
|
||||
payload[pos] = (msg[len-1] ^ hs6200_scramble[len-1]) << 7;
|
||||
}
|
||||
|
||||
void XN297_Configure(uint8_t flags)
|
||||
// crc
|
||||
if(hs6200_crc)
|
||||
{
|
||||
xn297_crc = !!(flags & BV(NRF24L01_00_EN_CRC));
|
||||
flags &= ~(BV(NRF24L01_00_EN_CRC) | BV(NRF24L01_00_CRCO));
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, flags);
|
||||
uint16_t crc = hs6200_calc_crc(&payload[hs6200_address_length+2], len+2);
|
||||
uint8_t hcrc = crc >> 8;
|
||||
uint8_t lcrc = crc & 0xff;
|
||||
payload[pos++] |= (hcrc >> 1);
|
||||
payload[pos++] = (hcrc << 7) | (lcrc >> 1);
|
||||
payload[pos++] = lcrc << 7;
|
||||
}
|
||||
|
||||
void XN297_WritePayload(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
uint8_t last = 0;
|
||||
NRF24L01_WritePayload(payload, pos);
|
||||
delayMicroseconds(option+20);
|
||||
NRF24L01_WritePayload(payload, pos);
|
||||
}
|
||||
//
|
||||
// End of HS6200 emulation
|
||||
////////////////////////////
|
||||
|
||||
if (xn297_addr_len < 4)
|
||||
{
|
||||
// If address length (which is defined by receive address length)
|
||||
// is less than 4 the TX address can't fit the preamble, so the last
|
||||
// byte goes here
|
||||
buf[last++] = 0x55;
|
||||
}
|
||||
for (uint8_t i = 0; i < xn297_addr_len; ++i)
|
||||
buf[last++] = xn297_tx_addr[xn297_addr_len-i-1] ^ xn297_scramble[i];
|
||||
///////////////
|
||||
// LT8900 emulation layer
|
||||
uint8_t LT8900_buffer[64];
|
||||
uint8_t LT8900_buffer_start;
|
||||
uint16_t LT8900_buffer_overhead_bits;
|
||||
uint8_t LT8900_addr[8];
|
||||
uint8_t LT8900_addr_size;
|
||||
uint8_t LT8900_Preamble_Len;
|
||||
uint8_t LT8900_Tailer_Len;
|
||||
uint8_t LT8900_CRC_Initial_Data;
|
||||
uint8_t LT8900_Flags;
|
||||
#define LT8900_CRC_ON 6
|
||||
#define LT8900_SCRAMBLE_ON 5
|
||||
#define LT8900_PACKET_LENGTH_EN 4
|
||||
#define LT8900_DATA_PACKET_TYPE_1 3
|
||||
#define LT8900_DATA_PACKET_TYPE_0 2
|
||||
#define LT8900_FEC_TYPE_1 1
|
||||
#define LT8900_FEC_TYPE_0 0
|
||||
|
||||
for (uint8_t i = 0; i < len; ++i) {
|
||||
// bit-reverse bytes in packet
|
||||
uint8_t b_out = bit_reverse(msg[i]);
|
||||
buf[last++] = b_out ^ xn297_scramble[xn297_addr_len+i];
|
||||
}
|
||||
if (xn297_crc)
|
||||
void LT8900_Config(uint8_t preamble_len, uint8_t trailer_len, uint8_t flags, uint8_t crc_init)
|
||||
{
|
||||
uint8_t offset = xn297_addr_len < 4 ? 1 : 0;
|
||||
uint16_t crc = 0xb5d2;
|
||||
for (uint8_t i = offset; i < last; ++i)
|
||||
crc = crc16_update(crc, buf[i]);
|
||||
crc ^= xn297_crc_xorout[xn297_addr_len - 3 + len];
|
||||
buf[last++] = crc >> 8;
|
||||
buf[last++] = crc & 0xff;
|
||||
}
|
||||
NRF24L01_WritePayload(buf, last);
|
||||
//Preamble 1 to 8 bytes
|
||||
LT8900_Preamble_Len=preamble_len;
|
||||
//Trailer 4 to 18 bits
|
||||
LT8900_Tailer_Len=trailer_len;
|
||||
//Flags
|
||||
// CRC_ON: 1 on, 0 off
|
||||
// SCRAMBLE_ON: 1 on, 0 off
|
||||
// PACKET_LENGTH_EN: 1 1st byte of payload is payload size
|
||||
// DATA_PACKET_TYPE: 00 NRZ, 01 Manchester, 10 8bit/10bit line code, 11 interleave data type
|
||||
// FEC_TYPE: 00 No FEC, 01 FEC13, 10 FEC23, 11 reserved
|
||||
LT8900_Flags=flags;
|
||||
//CRC init constant
|
||||
LT8900_CRC_Initial_Data=crc_init;
|
||||
}
|
||||
|
||||
void XN297_ReadPayload(uint8_t* msg, uint8_t len)
|
||||
void LT8900_SetChannel(uint8_t channel)
|
||||
{
|
||||
NRF24L01_ReadPayload(msg, len);
|
||||
for(uint8_t i=0; i<len; i++)
|
||||
msg[i] = bit_reverse(msg[i]) ^ bit_reverse(xn297_scramble[i+xn297_addr_len]);
|
||||
NRF24L01_WriteReg(NRF24L01_05_RF_CH, channel +2); //NRF24L01 is 2400+channel but LT8900 is 2402+channel
|
||||
}
|
||||
|
||||
// End of XN297 emulation
|
||||
void LT8900_SetTxRxMode(enum TXRX_State mode)
|
||||
{
|
||||
if(mode == TX_EN)
|
||||
{
|
||||
//Switch to TX
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_SetTxRxMode(TX_EN);
|
||||
//Disable CRC
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_PWR_UP));
|
||||
}
|
||||
else
|
||||
if (mode == RX_EN)
|
||||
{
|
||||
NRF24L01_WriteReg(NRF24L01_02_EN_RXADDR, 0x01); // Enable data pipe 0 only
|
||||
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, 32);
|
||||
//Switch to RX
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
NRF24L01_FlushRx();
|
||||
NRF24L01_SetTxRxMode(RX_EN);
|
||||
// Disable CRC
|
||||
NRF24L01_WriteReg(NRF24L01_00_CONFIG, (1 << NRF24L01_00_PWR_UP) | (1 << NRF24L01_00_PRIM_RX) );
|
||||
}
|
||||
else
|
||||
NRF24L01_SetTxRxMode(TXRX_OFF);
|
||||
}
|
||||
|
||||
void LT8900_BuildOverhead()
|
||||
{
|
||||
uint8_t pos;
|
||||
|
||||
//Build overhead
|
||||
//preamble
|
||||
memset(LT8900_buffer,LT8900_addr[0]&0x01?0xAA:0x55,LT8900_Preamble_Len-1);
|
||||
pos=LT8900_Preamble_Len-1;
|
||||
//address
|
||||
for(uint8_t i=0;i<LT8900_addr_size;i++)
|
||||
{
|
||||
LT8900_buffer[pos]=bit_reverse(LT8900_addr[i]);
|
||||
pos++;
|
||||
}
|
||||
//trailer
|
||||
memset(LT8900_buffer+pos,(LT8900_buffer[pos-1]&0x01)==0?0xAA:0x55,3);
|
||||
LT8900_buffer_overhead_bits=pos*8+LT8900_Tailer_Len;
|
||||
//nrf address length max is 5
|
||||
pos+=LT8900_Tailer_Len/8;
|
||||
LT8900_buffer_start=pos>5?5:pos;
|
||||
}
|
||||
|
||||
void LT8900_SetAddress(uint8_t *address,uint8_t addr_size)
|
||||
{
|
||||
uint8_t addr[5];
|
||||
|
||||
//Address size (SyncWord) 2 to 8 bytes, 16/32/48/64 bits
|
||||
LT8900_addr_size=addr_size;
|
||||
for (uint8_t i = 0; i < addr_size; i++)
|
||||
LT8900_addr[i] = address[addr_size-1-i];
|
||||
|
||||
//Build overhead
|
||||
LT8900_BuildOverhead();
|
||||
|
||||
//Set NRF RX&TX address based on overhead content
|
||||
NRF24L01_WriteReg(NRF24L01_03_SETUP_AW, LT8900_buffer_start-2);
|
||||
for(uint8_t i=0;i<LT8900_buffer_start;i++) // reverse bytes order
|
||||
addr[i]=LT8900_buffer[LT8900_buffer_start-i-1];
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_0A_RX_ADDR_P0, addr,LT8900_buffer_start);
|
||||
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr,LT8900_buffer_start);
|
||||
}
|
||||
|
||||
uint8_t LT8900_ReadPayload(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
uint8_t i,pos=0,shift,end,buffer[32];
|
||||
unsigned int a;
|
||||
crc=LT8900_CRC_Initial_Data;
|
||||
pos=LT8900_buffer_overhead_bits/8-LT8900_buffer_start;
|
||||
end=pos+len+(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN)?1:0)+(LT8900_Flags&_BV(LT8900_CRC_ON)?2:0);
|
||||
//Read payload
|
||||
NRF24L01_ReadPayload(buffer,end+1);
|
||||
//Check address + trail
|
||||
for(i=0;i<pos;i++)
|
||||
if(LT8900_buffer[LT8900_buffer_start+i]!=buffer[i])
|
||||
return 0; // wrong address...
|
||||
//Shift buffer to remove trail bits
|
||||
shift=LT8900_buffer_overhead_bits&0x7;
|
||||
for(i=pos;i<end;i++)
|
||||
{
|
||||
a=(buffer[i]<<8)+buffer[i+1];
|
||||
a<<=shift;
|
||||
buffer[i]=(a>>8)&0xFF;
|
||||
}
|
||||
//Check len
|
||||
if(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN))
|
||||
{
|
||||
crc16_update(buffer[pos],8);
|
||||
if(bit_reverse(len)!=buffer[pos++])
|
||||
return 0; // wrong len...
|
||||
}
|
||||
//Decode message
|
||||
for(i=0;i<len;i++)
|
||||
{
|
||||
crc16_update(buffer[pos],8);
|
||||
msg[i]=bit_reverse(buffer[pos++]);
|
||||
}
|
||||
//Check CRC
|
||||
if(LT8900_Flags&_BV(LT8900_CRC_ON))
|
||||
{
|
||||
if(buffer[pos++]!=((crc>>8)&0xFF)) return 0; // wrong CRC...
|
||||
if(buffer[pos]!=(crc&0xFF)) return 0; // wrong CRC...
|
||||
}
|
||||
//Everything ok
|
||||
return 1;
|
||||
}
|
||||
|
||||
void LT8900_WritePayload(uint8_t* msg, uint8_t len)
|
||||
{
|
||||
unsigned int a,mask;
|
||||
uint8_t i, pos=0,tmp, buffer[64], pos_final,shift;
|
||||
crc=LT8900_CRC_Initial_Data;
|
||||
//Add packet len
|
||||
if(LT8900_Flags&_BV(LT8900_PACKET_LENGTH_EN))
|
||||
{
|
||||
tmp=bit_reverse(len);
|
||||
buffer[pos++]=tmp;
|
||||
crc16_update(tmp,8);
|
||||
}
|
||||
//Add payload
|
||||
for(i=0;i<len;i++)
|
||||
{
|
||||
tmp=bit_reverse(msg[i]);
|
||||
buffer[pos++]=tmp;
|
||||
crc16_update(tmp,8);
|
||||
}
|
||||
//Add CRC
|
||||
if(LT8900_Flags&_BV(LT8900_CRC_ON))
|
||||
{
|
||||
buffer[pos++]=crc>>8;
|
||||
buffer[pos++]=crc;
|
||||
}
|
||||
//Shift everything to fit behind the trailer (4 to 18 bits)
|
||||
shift=LT8900_buffer_overhead_bits&0x7;
|
||||
pos_final=LT8900_buffer_overhead_bits/8;
|
||||
mask=~(0xFF<<(8-shift));
|
||||
LT8900_buffer[pos_final+pos]=0xFF;
|
||||
for(i=pos-1;i!=0xFF;i--)
|
||||
{
|
||||
a=buffer[i]<<(8-shift);
|
||||
LT8900_buffer[pos_final+i]=(LT8900_buffer[pos_final+i]&mask>>8)|a>>8;
|
||||
LT8900_buffer[pos_final+i+1]=(LT8900_buffer[pos_final+i+1]&mask)|a;
|
||||
}
|
||||
if(shift)
|
||||
pos++;
|
||||
//Send everything
|
||||
NRF24L01_WritePayload(LT8900_buffer+LT8900_buffer_start,pos_final+pos-LT8900_buffer_start);
|
||||
}
|
||||
// End of LT8900 emulation
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user