Compare commits
1017 Commits
Author | SHA1 | Date |
---|---|---|
Philip Howard | f3b53b6b5f | |
coadkins | 37c4d22527 | |
Philip Howard | 616b1cc8d6 | |
Phil Howard | 45a9925072 | |
Connor Linfoot | 32c10482d9 | |
Philip Howard | 4c44b77193 | |
Phil Howard | 5510c82564 | |
Philip Howard | 3a10b29f54 | |
Phil Howard | 8cf276b992 | |
Philip Howard | f1ea35fbbf | |
Philip Howard | c066325ca0 | |
Philip Howard | fd4eb165f8 | |
Phil Howard | 8fc8a8ee06 | |
Phil Howard | 3bfb548686 | |
Philip Howard | 9edcdcc126 | |
Philip Howard | e8e550b18b | |
thirdr | cdb7b4bf2c | |
Philip Howard | 4fc3095433 | |
Phil Howard | 9c5b529754 | |
ZodiusInfuser | a87d5581aa | |
ZodiusInfuser | 44d7875f7e | |
ZodiusInfuser | a90c31fb3b | |
ZodiusInfuser | 458b0ac209 | |
Phil Howard | a537672dd4 | |
Phil Howard | d34e692f51 | |
Phil Howard | 27b913124c | |
Phil Howard | c7b788cd1d | |
Philip Howard | c386b3e9cf | |
Philip Howard | a7a2e2bee0 | |
Phil Howard | 19fa8864cf | |
Phil Howard | 964cf5eedf | |
Phil Howard | eab1595352 | |
Phil Howard | 5dd76ed31b | |
Philip Howard | 6eb0f90e53 | |
Phil Howard | b0d53dadb3 | |
Phil Howard | ad518064e9 | |
Philip Howard | d83107474e | |
Phil Howard | c4f70df1cf | |
Phil Howard | 10221066dd | |
Philip Howard | ab64fcaccc | |
Hel Gibbons | 32c63c343d | |
Hel Gibbons | 8d964bce2c | |
ZodiusInfuser | 8ca47d6405 | |
Skyler Mansfield | b23a71b889 | |
Philip Howard | 6b23c1526d | |
Phil Howard | c19b2276f1 | |
Philip Howard | 392d75b00d | |
Philip Howard | 911cbb710e | |
Philip Howard | 4e3e2c836d | |
Philip Howard | 5bd5334379 | |
Philip Howard | 9ddbb17a82 | |
Phil Howard | 5126263f91 | |
Philip Howard | 0d3fce9b9d | |
Rob Berwick | 9e6a0725c0 | |
Rob Berwick | 3e81b245a1 | |
Phil Howard | bd6bd289d1 | |
Phil Howard | b6953c25a1 | |
Phil Howard | b5df0ac277 | |
Phil Howard | 116bbb1296 | |
Phil Howard | 6154116662 | |
Phil Howard | d45daef654 | |
Phil Howard | 1b3d9d9fb2 | |
Phil Howard | 4dd76525f6 | |
Phil Howard | 3bac13fcc8 | |
Hel Gibbons | bff245324b | |
Hel Gibbons | 400347b862 | |
Ray Bellis | da0ac1821f | |
Rob Berwick | 6dcc0d4fa0 | |
Erin Sparling | fc3f8e5654 | |
Erin Sparling | c001f9bb59 | |
Erin Sparling | 59fa0a1ff8 | |
Stefan Werder | a803c3cee4 | |
Ray Bellis | 6fd667b1ca | |
Paco Hope | 078d81312f | |
Hel Gibbons | a60c856ea8 | |
Philip Howard | b4451c3bdc | |
Phil Howard | ce42d814a7 | |
Philip Howard | ee7f2758dd | |
Phil Howard | 388d8af3dc | |
Phil Howard | 0f75a2839f | |
Phil Howard | e691628723 | |
Phil Howard | 08ce4fbb81 | |
Phil Howard | 20de8a3198 | |
Philip Howard | 9499b7e908 | |
Philip Howard | 65fd3b1c5a | |
Phil Howard | 4b3e83f2ff | |
Philip Howard | fc777ff0ca | |
Hel Gibbons | 5345cc42d2 | |
Hel Gibbons | 169ed9c763 | |
Philip Howard | 8eac60afc6 | |
Phil Howard | 93525a422f | |
Hel Gibbons | 36f3e3a69f | |
Hel Gibbons | 29a13305d2 | |
Philip Howard | 1f4704afdb | |
Phil Howard | ae7e6e8c6c | |
Phil Howard | 5f730ff400 | |
Philip Howard | c3919bd648 | |
Philip Howard | ed3ce45f00 | |
Philip Howard | 1a7deaab71 | |
Philip Howard | 9735402ee3 | |
Philip Howard | c045c405c3 | |
Mike Bell | 80e1e16782 | |
Mike Bell | cdd648f3f6 | |
Mike Bell | 81f42f25b6 | |
Mike Bell | 841c141ebf | |
Mike Bell | c812eec432 | |
Mike Bell | 41eb2b503e | |
Mike Bell | 581481c2ef | |
Mike Bell | e908d5e53e | |
Mike Bell | 34b8ac9f0c | |
Philip Howard | 9124b376d2 | |
Phil Howard | c725b4eee0 | |
Philip Howard | a334899b61 | |
Phil Howard | cca2d569e4 | |
Philip Howard | 3d8f8c9a83 | |
Phil Howard | 788f6c3232 | |
Phil Howard | 231ceb70f2 | |
Phil Howard | c9fd68ec58 | |
Hel Gibbons | b82429caf0 | |
Hel Gibbons | 386a3594c0 | |
Hel Gibbons | 9e0b3adc0c | |
Pete Favelle | 8c3a21ec1a | |
Pete Favelle | 2f44e85431 | |
Phil Howard | 5a92a9c735 | |
Pete Favelle | 8a9ef39158 | |
Phil Howard | c443f8d206 | |
Phil Howard | 591058fb12 | |
Phil Howard | cfe8b3c096 | |
Phil Howard | 9d0501a43c | |
Phil Howard | 7c5ebfce8c | |
Phil Howard | 61c9d7e9b6 | |
Phil Howard | c7d9fe411a | |
Phil Howard | 4671607b3a | |
Phil Howard | 95ab839ba5 | |
Phil Howard | 9e430fd68c | |
Phil Howard | c9a8d5ef49 | |
Phil Howard | 38aaa04c5d | |
Phil Howard | e8dba75aff | |
Phil Howard | 09a58b269f | |
Phil Howard | cc7219b44a | |
Philip Howard | 57042bfed3 | |
Hel Gibbons | dc4ee0d459 | |
Hel Gibbons | 157180c476 | |
Hel Gibbons | 7344e4d1a4 | |
Phil Howard | 1157e605a1 | |
Philip Howard | b82d16e8ae | |
Hel Gibbons | 095122c606 | |
Mike Bell | 211e0aa618 | |
Mike Bell | b8116fc371 | |
Mike Bell | 0cfcb22aa4 | |
Mike Bell | 3cdfe558e8 | |
Mike Bell | 103228a88d | |
Mike Bell | 3a5f069ec1 | |
Mike Bell | 765b8a6226 | |
Mike Bell | 3c2c7ccc94 | |
Mike Bell | b9cd998709 | |
Mike Bell | 1a54f7b77d | |
Mike Bell | 8f78e3d6bc | |
Mike Bell | a396512e7f | |
Mike Bell | 9a0b21d417 | |
Mike Bell | e9779fc0e7 | |
Mike Bell | 559ce08e04 | |
Phil Howard | cbc05863c0 | |
Phil Howard | 7d8bbf5c08 | |
Mike Bell | 7e9f16d80c | |
Mike Bell | 4b57162c06 | |
Phil Howard | de4aaa80b6 | |
Phil Howard | 4afe062d19 | |
Mike Bell | 3bc215074c | |
Mike Bell | a6bd626334 | |
Mike Bell | 575e806cc8 | |
Mike Bell | be943bd5a0 | |
Mike Bell | daf7232024 | |
Mike Bell | a7435c6a5e | |
Mike Bell | 5a6aa0186c | |
Mike Bell | 360588ff67 | |
Mike Bell | 4ed1d61336 | |
Mike Bell | 31b480d138 | |
Mike Bell | c7049f4ff1 | |
Mike Bell | 1d8c836635 | |
Mike Bell | e295e69c72 | |
Mike Bell | 5971bc9ad8 | |
Mike Bell | a1caa9495c | |
Mike Bell | f4b8bc9025 | |
Mike Bell | 2e8632f2b6 | |
Mike Bell | da36b0ad32 | |
Mike Bell | 9acc270418 | |
Mike Bell | 5f8e7556f0 | |
Philip Howard | a7efef0049 | |
Hel Gibbons | 5a5786d066 | |
Simon Prickett | 710863099d | |
Simon Prickett | 316957c263 | |
Simon Prickett | 9b18ed2594 | |
Simon Prickett | 658025a99b | |
Hel Gibbons | fef22530ea | |
Angus Logan | fc28845fb9 | |
Andrew Wilkinson | 14c7f6c9c8 | |
Simon Prickett | 14eabf360f | |
Simon Prickett | fe805a711a | |
Philip Howard | d93839d56a | |
Philip Howard | e1527c44d5 | |
Phil Howard | 4ad6df5cc3 | |
Phil Howard | 004c8de8eb | |
Philip Howard | 3639b46881 | |
Phil Howard | b744f78a46 | |
Philip Howard | 5bc85c0e6d | |
ZodiusInfuser | 6bd5a445ba | |
Irvin | 6306d5e753 | |
Irvin | f0bfc7c13b | |
Irvin | e1e467185a | |
Irvin | 7e65c15cfb | |
Irvin Makosa | 462724210c | |
Irvin Makosa | 0b0474e062 | |
Irvin Makosa | 90a2076b7b | |
Irvin Makosa | e14903dd27 | |
Irvin Makosa | f06d1035a4 | |
Hel Gibbons | d0c40af766 | |
Hel Gibbons | 439b97d96e | |
Irvin Makosa | 8bb5e17e65 | |
Irvin | b8cdff8f4f | |
Hel Gibbons | eb31f9b043 | |
Phil Howard | b368950f02 | |
Philip Howard | 51574f839d | |
Hel Gibbons | 03232bbeb5 | |
Phil Howard | 5ec6903f7f | |
helgibbons | c696cd5e2d | |
Phil Howard | 6db7a9a0a6 | |
Phil Howard | 1630ddbbb2 | |
Hel Gibbons | 25237c54ce | |
Alexander Wilde | 16c2dc0356 | |
Hel Gibbons | 8f5a94482b | |
thinkier | aa8b158ba3 | |
Hel Gibbons | 52df18e550 | |
Hel Gibbons | 1d1b521dfb | |
Hel Gibbons | 3786cbdfe6 | |
Hel Gibbons | 58cdc85b1f | |
helgibbons | 6b67f652c7 | |
helgibbons | 37638172ae | |
helgibbons | 3236503805 | |
helgibbons | ce24330842 | |
helgibbons | 130685eeeb | |
Hel Gibbons | 6afdfef45b | |
Angus Logan | 1e6e68356a | |
Angus Logan | d25c6953d0 | |
Angus Logan | 0a0b72701e | |
Hel Gibbons | 40f0554259 | |
Hel Gibbons | d9064f0162 | |
Hel Gibbons | dc1f000134 | |
Hel Gibbons | 32ae70d16d | |
Hel Gibbons | 8a6bb65d73 | |
LionsPhil | a0fe954b7c | |
Hel Gibbons | 951fe4d8b8 | |
Hel Gibbons | cbaf1fa27d | |
Hel Gibbons | 16f8f0ab05 | |
Hel Gibbons | d759522b08 | |
Hel Gibbons | 9307ea1360 | |
Philip Howard | 6fb35df544 | |
Phil Howard | b0d63ef777 | |
Philip Howard | d523eded0b | |
Phil Howard | 090ce9d2c6 | |
Philip Howard | 9d96d061e9 | |
Phil Howard | 70a1b26041 | |
Phil Howard | bff6bd023e | |
Phil Howard | 19c57ebb20 | |
Phil Howard | 94c5d74894 | |
ZodiusInfuser | 67152e32e5 | |
ZodiusInfuser | 7aa75e57a4 | |
ZodiusInfuser | 68f610184f | |
ZodiusInfuser | aabe789f21 | |
ZodiusInfuser | bd4238945d | |
ZodiusInfuser | ae252fbc6e | |
Philip Howard | d4609699ba | |
Philip Howard | 74064407e9 | |
Phil Howard | 0a2e099886 | |
Hel Gibbons | c8d3b6b7d1 | |
Hel Gibbons | 5aa227ff45 | |
Phil Howard | fba7b53c36 | |
Philip Howard | 00d1617947 | |
Phil Howard | 652de85f4d | |
Philip Howard | 8648196cc2 | |
Philip Howard | ec205fb045 | |
Mike Bell | 5b31e018ff | |
ZodiusInfuser | d00185d831 | |
ZodiusInfuser | 0120975b3c | |
ZodiusInfuser | 12e38c1157 | |
Philip Howard | 89699fd78f | |
Phil Howard | 05cad0c157 | |
ZodiusInfuser | 653090c89e | |
Philip Howard | 1c39da4997 | |
Philip Howard | 092fbe472f | |
Philip Howard | ecbd1e66de | |
Ray Bellis | bfb6490ec8 | |
Ray Bellis | 32dfdc6a20 | |
Ray Bellis | 67df015bfe | |
Hel Gibbons | 302d6ae0eb | |
Hel Gibbons | 1be888f397 | |
Philip Howard | 7491ced13b | |
Niko Kotilainen | 1dcad21ed2 | |
ZodiusInfuser | 8966cbf348 | |
Pete Favelle | a59aa35df5 | |
Philip Howard | b6499e71fc | |
ZodiusInfuser | 5619274d3d | |
ZodiusInfuser | 862806f357 | |
Phil Howard | 7951ef9668 | |
ZodiusInfuser | 4dadeb0d4d | |
Phil Howard | b30d9ca554 | |
ZodiusInfuser | e0a405739c | |
ZodiusInfuser | 9f925b5259 | |
Phil Howard | 8648597134 | |
ZodiusInfuser | e3f9f14dcf | |
ZodiusInfuser | 226e7507dd | |
ZodiusInfuser | 1cfae8b5f8 | |
ZodiusInfuser | 387df3bd12 | |
Phil Howard | 45a2e0f5b1 | |
Phil Howard | e90ae33a99 | |
ZodiusInfuser | d4d6cd1936 | |
Phil Howard | dd4347dac3 | |
ZodiusInfuser | 928c28b677 | |
ZodiusInfuser | 2d45ed6ace | |
ZodiusInfuser | 178afd1469 | |
ZodiusInfuser | 6464f44437 | |
ZodiusInfuser | cd83a51e8a | |
ZodiusInfuser | f353525090 | |
ZodiusInfuser | 7c11593f7c | |
ZodiusInfuser | e3c3692e31 | |
ZodiusInfuser | 15978e5ddc | |
ZodiusInfuser | 59d57a193b | |
Philip Howard | 56dba370c6 | |
Phil Howard | 22a659a559 | |
Phil Howard | 70f133dd62 | |
Philip Howard | f0975778f6 | |
Phil Howard | c885789ada | |
Phil Howard | 8e0fe155c5 | |
Hel Gibbons | b512bdcea1 | |
andrewmk | 49e0b25557 | |
Phil Howard | c25de67247 | |
Philip Howard | 334283a8f6 | |
Phil Howard | af99d6d5f8 | |
Philip Howard | 5bfc0eba4c | |
Phil Howard | 1d003a80c2 | |
Phil Howard | a19f5943c3 | |
Hel Gibbons | cb5dcb965e | |
Hel Gibbons | f48c17ae1f | |
phennessey7 | d78fffcd54 | |
phennessey7 | 7b7959ef20 | |
Hel Gibbons | 39eae8d42c | |
Quitsoon | f6206876b7 | |
mutatrum | 7bdd85d677 | |
Philip Howard | 7b7352130f | |
Philip Howard | 40d4774cf6 | |
Phil Howard | 3a35013667 | |
Phil Howard | 3bdb27458f | |
Phil Howard | cce02feabd | |
Phil Howard | aeca08f275 | |
Phil Howard | d69797689a | |
Phil Howard | af352ff33f | |
Phil Howard | 883d751f52 | |
Hel Gibbons | 5fa99c0690 | |
Hel Gibbons | 75602b3fbb | |
Hel Gibbons | 6e7ec899eb | |
Phil Howard | acf0e568bd | |
Philip Howard | c9bee93372 | |
Phil Howard | 540bc2f75f | |
Phil Howard | 761e8b5c3c | |
Phil Howard | 07bc005958 | |
Phil Howard | bad6a9e8d6 | |
Phil Howard | 049a121974 | |
Phil Howard | cc5a2bdb6f | |
Phil Howard | 140fe913ae | |
Philip Howard | 64941584d1 | |
Phil Howard | 569bcadcae | |
Philip Howard | 9f71c04ea8 | |
Phil Howard | 0d0dc6a781 | |
Philip Howard | 2303702c15 | |
Philip Howard | d9d71c663f | |
Phil Howard | 0148e5f5e7 | |
Phil Howard | f5859ad371 | |
Phil Howard | 393879581a | |
Philip Howard | c6b4a30c09 | |
Hel Gibbons | fbcd7836de | |
Janos P Toth | be1b827cfc | |
Phil Howard | 2c0310ca9a | |
Phil Howard | 667faf70d1 | |
Hel Gibbons | 1da4472926 | |
Hel Gibbons | 139569c2f3 | |
Philip Howard | aa70394495 | |
Hel Gibbons | 0666dc885a | |
Hel Gibbons | 5392c5aa03 | |
Philip Howard | cb5db1d594 | |
Phil Howard | 587588dca5 | |
Phil Howard | bb004e03a6 | |
Phil Howard | f9b46ba3a7 | |
helgibbons | ec4c6c83f4 | |
helgibbons | 6f06ab246c | |
Phil Howard | 1b0b783a2f | |
helgibbons | 9aa0465d8b | |
Phil Howard | b83bdbf198 | |
Phil Howard | f2751ba6e9 | |
Phil Howard | 3eb42336e6 | |
Phil Howard | 044313551b | |
Phil Howard | bd3651d97d | |
Phil Howard | 9964ed716b | |
Phil Howard | af2b74d65d | |
Phil Howard | a45eeb1623 | |
Phil Howard | aa91450f59 | |
Phil Howard | dd7ea6fdc3 | |
Phil Howard | e4cb7ce95f | |
Phil Howard | 157841f9db | |
Phil Howard | a0ab44067f | |
Phil Howard | 1f0302bd66 | |
Phil Howard | bcebccca1d | |
Phil Howard | 59ae107982 | |
Phil Howard | 73f50e43ec | |
Phil Howard | 375df60ff3 | |
Phil Howard | b74b371d2b | |
Hel Gibbons | d0e2425e07 | |
Hel Gibbons | 60fe949fc5 | |
Hel Gibbons | d4d2b6fcbc | |
Hel Gibbons | 0e6dbc1a6c | |
Hel Gibbons | 9bee6d7431 | |
Michael Mogenson | 2f26c172ed | |
Hel Gibbons | 2db116ccec | |
Hel Gibbons | 9ed2c40808 | |
Hel Gibbons | 9730fee753 | |
Hel Gibbons | 0138980849 | |
helgibbons | 6f73f8fc83 | |
Philip Howard | 94d5b0cd33 | |
Phil Howard | 13599b55a1 | |
Philip Howard | faf4efac34 | |
Phil Howard | a448043870 | |
Phil Howard | 75d56d04ad | |
Phil Howard | f255f419a1 | |
Phil Howard | 3f92caee22 | |
Hel Gibbons | 3d597e7d03 | |
Phil Howard | a8a8321405 | |
Phil Howard | 83f88c034d | |
Phil Howard | aed14aca22 | |
Phil Howard | cacb5749ae | |
Hel Gibbons | 789e63bd81 | |
Anton Mosich | aee0a2879b | |
Phil Howard | 63d4c23cd5 | |
Phil Howard | a6e35e207d | |
Phil Howard | 07a5aac48f | |
Hel Gibbons | f194715108 | |
Phil Howard | 56e5878b62 | |
Phil Howard | b6d0e54803 | |
Phil Howard | cb39d5c0f3 | |
Phil Howard | 9e120995b2 | |
Phil Howard | f4f5c6319a | |
Phil Howard | 64632559f9 | |
Philip Howard | 6be46dd429 | |
LionsPhil | 0a6d6b91b3 | |
Hel Gibbons | 3d96ff9d92 | |
Hel Gibbons | 421e715f06 | |
Hel Gibbons | 8e91108ce5 | |
thirdr | b66fc524ae | |
thirdr | 0cec01cf37 | |
Hel Gibbons | 1dbd3ea312 | |
Brian Corteil | f882efc901 | |
Hel Gibbons | bc49a69416 | |
Philip Howard | 5abbf9b5c8 | |
Philip Howard | f1809f3ebe | |
Hel Gibbons | 5ca10794a8 | |
Hel Gibbons | 1cb170664e | |
Phil Howard | 07a1bf645a | |
thirdr | 45a12892b8 | |
Hel Gibbons | de6b7e2828 | |
Hel Gibbons | 579b8daa4e | |
Phil Howard | 0fda232dcb | |
Phil Howard | cbc21e52be | |
Phil Howard | b4d72d250d | |
Phil Howard | 10ea8757ea | |
Phil Howard | c836f9f8fe | |
Phil Howard | d750532180 | |
Phil Howard | 5f3f14e5ce | |
Phil Howard | ad5e4cc10e | |
Hel Gibbons | ab45fee123 | |
Hel Gibbons | ec601de9a4 | |
Hel Gibbons | 6aa5aaf50c | |
Hel Gibbons | 8069ae0e8f | |
Hel Gibbons | 3459cc5f62 | |
Hel Gibbons | 7b8b2796ed | |
Hel Gibbons | 931b3b26b2 | |
Hel Gibbons | 6fc8ebd024 | |
Hel Gibbons | b6042e78c1 | |
Hel Gibbons | c194fc7a2b | |
Hel Gibbons | 3e44edf66a | |
Hel Gibbons | c2b1eb47e5 | |
Hel Gibbons | 86c304d192 | |
thirdr | b762ee8861 | |
thirdr | 36458436bc | |
thirdr | d29666b87a | |
thirdr | 797c0bbfd4 | |
thirdr | 9f8596c031 | |
thirdr | 2357e8225b | |
thirdr | bc89afb58c | |
thirdr | e8ff06dac8 | |
thirdr | 1fe071b713 | |
thirdr | 44cc9d6e5e | |
thirdr | 66fbdde5e1 | |
thirdr | 93dff218dc | |
Philip Howard | 9c1c9e6b8d | |
Philip Howard | c3498750ae | |
thirdr | d25b04c93f | |
Phil Howard | c9d39d813c | |
Gee Bartlett | ca9b9d6b6a | |
Hel Gibbons | 35cc345eb4 | |
Hel Gibbons | 69ee5cff3a | |
Hel Gibbons | 9a613975d7 | |
Gee Bartlett | 4bc002091e | |
Brian Corteil | 37ff6b8b37 | |
Philip Howard | d4bb43a537 | |
Phil Howard | 52f9c33874 | |
Philip Howard | 9ae42fdaab | |
Phil Howard | 0c0a7b3bf6 | |
Philip Howard | aab8f0be35 | |
Phil Howard | 8ff40fae6e | |
Philip Howard | 206d4089d7 | |
ZodiusInfuser | 446d7189c6 | |
Gee Bartlett | 0616a0694e | |
Philip Howard | 9dbd25bd51 | |
Phil Howard | bfb2f8d2ba | |
Phil Howard | bff2e79a56 | |
Phil Howard | a5b0633469 | |
Phil Howard | 80fab77270 | |
Phil Howard | c9f6dfec4f | |
Phil Howard | 3e0cd28876 | |
Philip Howard | 63de02016f | |
Phil Howard | bea90dfd60 | |
Phil Howard | d3a1a571d3 | |
Phil Howard | 2ff5d462c8 | |
Philip Howard | 1317f2e04e | |
Phil Howard | 8047f29ba6 | |
Martin Budden | e66a2893ed | |
Phil Howard | 5f9d4e33b3 | |
Gee Bartlett | 552339a49e | |
Gee Bartlett | 8637967605 | |
Gee Bartlett | 93a18a874a | |
Gee Bartlett | 979cc38b2a | |
Gee Bartlett | 96c83f8ae4 | |
Gee Bartlett | 2a1c1e464f | |
Gee Bartlett | 58ee3b1e97 | |
Philip Howard | f17c04b7ff | |
Philip Howard | b08cf1fba0 | |
Hel Gibbons | 8c4e54ad6b | |
Brian Corteil | a5538da8e6 | |
Brian Corteil | ba7a17ff43 | |
Zoobdude | b4be3014fc | |
Zoobdude | 3797d4b76e | |
thirdr | e3dc655a34 | |
Philip Howard | af46e0379f | |
Philip Howard | c78e85a171 | |
Phil Howard | 85881db00c | |
Phil Howard | 81884448ab | |
Philip Howard | 049219c7f4 | |
Phil Howard | 2176e0feb1 | |
Phil Howard | fc37ef100d | |
Phil Howard | a09fde770f | |
Phil Howard | 6c0c45d6c8 | |
Phil Howard | f4b0434229 | |
Phil Howard | bbeac41785 | |
Phil Howard | aa9c5b6538 | |
Phil Howard | ff3efc0d28 | |
Phil Howard | 72ad4dcc47 | |
Phil Howard | 1b2dae45d3 | |
Gee Bartlett | 53162737a1 | |
Gee Bartlett | 9bc616690e | |
lowfatcode | c3672d7e3d | |
Philip Howard | 93ac854672 | |
Hel Gibbons | f06d7c2e83 | |
Zoobdude | ae908c6075 | |
Philip Howard | 1b87ec8bd8 | |
Phil Howard | ab488a4452 | |
Phil Howard | 00988c6207 | |
Phil Howard | b0c25a57a2 | |
Phil Howard | 776383c111 | |
Phil Howard | 88e50891d1 | |
Phil Howard | 4928ceff8d | |
Phil Howard | 1659139c97 | |
Phil Howard | 43382ba2c0 | |
Phil Howard | 93979cb735 | |
AndrewCapon | 0067b101a0 | |
Philip Howard | b810ffdfdb | |
Phil Howard | d34171381c | |
Hel Gibbons | 68411ba6ec | |
Hel Gibbons | de48394764 | |
Hel Gibbons | 259bad7ce8 | |
Hel Gibbons | 740aa14cac | |
Phil Howard | fd2f285f16 | |
Hel Gibbons | fa275e6919 | |
Hel Gibbons | dc121aa4f1 | |
Hel Gibbons | ed021665e6 | |
Philip Howard | 3bc0f019b2 | |
Phil Howard | 25efb91eae | |
ZodiusInfuser | 943ea1390a | |
ZodiusInfuser | 5a62a79a93 | |
helgibbons | 2bdb05ce68 | |
Brian Corteil | 47a8373bbb | |
Hel Gibbons | 8a420ca0e4 | |
Hel Gibbons | 9bba17cc47 | |
Philip Howard | 05391d27cd | |
Phil Howard | cb82878cd2 | |
LionsPhil | 033157503b | |
Phil Howard | 2952fd760e | |
Phil Howard | 0410b7c775 | |
Phil Howard | 3f0efa9765 | |
Phil Howard | d7b9881c0f | |
Phil Howard | a275f31c7a | |
Phil Howard | 3a2a43f4b3 | |
Phil Howard | cbcd9edd9a | |
Mike Bell | 26eeb2b042 | |
Phil Howard | 2109d7fb37 | |
Phil Howard | 9ea196adb1 | |
Phil Howard | a70ce8a4a6 | |
Phil Howard | 16e99a5a89 | |
Hel Gibbons | d4273ebbcb | |
Phil Howard | 7a2ffccf16 | |
Phil Howard | 548e1fdbdb | |
Phil Howard | b5fb62c8f6 | |
Phil Howard | c56844ae69 | |
Phil Howard | c3ad87765d | |
thirdr | 6e44434e7d | |
Phil Howard | de8ed26460 | |
Phil Howard | 6349d45768 | |
Phil Howard | d8f9b81eca | |
Phil Howard | 23f792caa9 | |
thirdr | 379a1cb3ef | |
Phil Howard | 6526787772 | |
Phil Howard | 15dabdba1f | |
Phil Howard | a02834be9e | |
Phil Howard | f82e21018f | |
Phil Howard | 1dbc992180 | |
Phil Howard | 03d5224e3e | |
Phil Howard | d1646e5d76 | |
Phil Howard | e859b39e11 | |
Phil Howard | 2b3b1e4676 | |
Phil Howard | 984dbd5185 | |
Phil Howard | 9bd29e0ef2 | |
Phil Howard | 81d9b2ab81 | |
Phil Howard | ac2da23c96 | |
Phil Howard | c70043922a | |
Phil Howard | 21b4ff68d7 | |
Phil Howard | 6ea105bb03 | |
Phil Howard | d25324d6c7 | |
Philip Howard | de8cb95ab4 | |
Philip Howard | 5e135b8901 | |
Phil Howard | 1fc00ac5af | |
Philip Howard | 029b697cb1 | |
Mike Bell | 3371b6c751 | |
Philip Howard | c4decc5003 | |
Philip Howard | cd2f45dee4 | |
Simon Shirley | cd95edc707 | |
ZodiusInfuser | cacfbd174b | |
ZodiusInfuser | eda6e996ce | |
ZodiusInfuser | 1343f23316 | |
Pete Favelle | d84d9f4520 | |
Simon Shirley | 9aedf16bb5 | |
Simon Shirley | 77ee234caa | |
Simon Shirley | eb7eaae885 | |
Philip Howard | 884722de50 | |
Philip Howard | d52fe23d81 | |
Phil Howard | b548d67609 | |
Phil Howard | 2d28880529 | |
Phil Howard | 9bf6bdde06 | |
Philip Howard | 397ef786a9 | |
Hel Gibbons | db5004d51c | |
Phil Howard | 6bf5c7f7b5 | |
Hel Gibbons | 7a6857b371 | |
Hel Gibbons | 1ece897490 | |
Hel Gibbons | 9aa2e33d46 | |
Hel Gibbons | 92d58a17c0 | |
Hel Gibbons | d2da1c6213 | |
Hel Gibbons | b3edbc47d0 | |
Hel Gibbons | 914a4b345c | |
Philip Howard | 39c4848bcc | |
Philip Howard | cb5ad29ee1 | |
Philip Howard | 63fc338ca6 | |
Philip Howard | c98d0daaf4 | |
thirdr | 50b456d42f | |
Philip Howard | 62d46210a3 | |
Phil Howard | 0709878cec | |
thirdr | e0d578bd07 | |
Phil Howard | 828ff8453d | |
Phil Howard | c46320656d | |
Phil Howard | 0e1b37d36a | |
Phil Howard | c7bdf4a3cc | |
Phil Howard | fb6fdcc115 | |
Phil Howard | c42679b355 | |
Phil Howard | 9bf1583787 | |
Phil Howard | 04888da334 | |
Phil Howard | ff5dc0078f | |
Phil Howard | 8b1bd814e5 | |
Phil Howard | 5f94ee7acc | |
Phil Howard | a4a5b58d93 | |
Phil Howard | 5b3f84ebf0 | |
Phil Howard | 77a5edc83f | |
Phil Howard | a29ce1e180 | |
Phil Howard | a612e66779 | |
Phil Howard | b041b546e6 | |
Hel Gibbons | 84cd34b24f | |
Hel Gibbons | 80c93e7067 | |
Hel Gibbons | 45014853e6 | |
Hel Gibbons | 1e1d8bf4f5 | |
ZodiusInfuser | d432948a4c | |
thirdr | 97f2232e07 | |
thirdr | 14af0fd646 | |
thirdr | 3a2e212e36 | |
ZodiusInfuser | b4d1c42669 | |
Philip Howard | 955f4e57a4 | |
thirdr | f4ab44489c | |
Philip Howard | f4a86fb5e5 | |
thirdr | 1c03a48e19 | |
ZodiusInfuser | 4c0a34769d | |
thirdr | 417f5d9210 | |
thirdr | 192625956b | |
thirdr | e2e4687f0c | |
thirdr | daf2770d28 | |
thirdr | 16e8e0bcd4 | |
thirdr | da52a1ab92 | |
thirdr | a11d2b6fc0 | |
thirdr | 5a89f5be97 | |
thirdr | 3a3b82889d | |
thirdr | be39aafd2d | |
ZodiusInfuser | 4273e9743e | |
thirdr | 1c88dcca40 | |
thirdr | 3749f19756 | |
thirdr | db4473ff5e | |
thirdr | 227317f4fb | |
ZodiusInfuser | 3bf10632c2 | |
Philip Howard | a46a53a709 | |
ZodiusInfuser | 944830f616 | |
Phil Howard | 940b31bab1 | |
Phil Howard | e5bfe76970 | |
Phil Howard | 2ea5de97df | |
Phil Howard | 5251baf764 | |
Phil Howard | f1e9e00cd5 | |
Phil Howard | ba100ae069 | |
ZodiusInfuser | 05d26aeecb | |
ZodiusInfuser | 43ef76b945 | |
Philip Howard | 6f74017894 | |
Charlie Birks | 52164a1c34 | |
Philip Howard | fe55d9beef | |
Philip Howard | 3891eef94f | |
Philip Howard | 5a2a62b471 | |
Philip Howard | ec0077ae71 | |
Philip Howard | 930784df08 | |
Gee Bartlett | 104c819412 | |
Phil Howard | 13414b5037 | |
housten | 28b53f174e | |
Hel Gibbons | a9d9c1089e | |
Phil Howard | 241e725464 | |
Phil Howard | e0a1627fba | |
Hel Gibbons | d5e1a2d8a0 | |
Hel Gibbons | 50b8e2ef90 | |
Pete Favelle | b81c43f479 | |
Pete Favelle | 875119da70 | |
Hel Gibbons | b1e8ed0864 | |
helgibbons | a62c3f0e20 | |
helgibbons | 95252f2de5 | |
helgibbons | 7ac2a285d8 | |
helgibbons | 7555ac97fd | |
helgibbons | 3b0317b118 | |
helgibbons | 963702a6e5 | |
helgibbons | d612f4b4dc | |
Hel Gibbons | 993609342e | |
helgibbons | be76ec01af | |
helgibbons | 96aa82fcb1 | |
Gee Bartlett | 7b127d5f19 | |
Hel Gibbons | 62303b6912 | |
Eric Sorenson | 2b7a77e110 | |
Hel Gibbons | 4b3d39bf44 | |
helgibbons | 6eb62b1a67 | |
helgibbons | 660fcea744 | |
Hel Gibbons | 40945c4cee | |
Phil Howard | 36401e174e | |
helgibbons | 8bee978d0b | |
helgibbons | e3496efda0 | |
helgibbons | 59ed91c755 | |
helgibbons | ff2bb4f026 | |
Hel Gibbons | 6ebf1a97f8 | |
helgibbons | 4f8eb8fd20 | |
John McCabe | e2d330e9c9 | |
helgibbons | 22774b88c7 | |
helgibbons | 78baea7ef5 | |
Hel Gibbons | be1c39e80c | |
Hel Gibbons | 55821d2896 | |
ZodiusInfuser | f1d9ec7c61 | |
ZodiusInfuser | 23f56f05e4 | |
Gee Bartlett | 4f4b0b277c | |
ZodiusInfuser | 91a2e7f5fb | |
Hel Gibbons | 31ecfef156 | |
Hel Gibbons | 98e29e07fb | |
Gee Bartlett | cf88add1ad | |
Gee Bartlett | 7a458f32e4 | |
Hel Gibbons | 065b55b6af | |
Gee Bartlett | b3893d0052 | |
ZodiusInfuser | 678b8d7cf9 | |
Gee Bartlett | f3f260e176 | |
Hel Gibbons | df881f12aa | |
ZodiusInfuser | 64b1a8c2d5 | |
ZodiusInfuser | 408d105e8b | |
ZodiusInfuser | 3765d43ce0 | |
ZodiusInfuser | c247770f48 | |
ZodiusInfuser | d11c73d45e | |
Gee Bartlett | 61a80ce66e | |
Gee Bartlett | eef9334805 | |
Gee Bartlett | 78101c47b5 | |
Hel Gibbons | c2b695f7e9 | |
Hel Gibbons | 4319570960 | |
Hel Gibbons | 0e80e1d96a | |
Hel Gibbons | 6d98cec7de | |
Hel Gibbons | 080da22b5f | |
Hel Gibbons | 91505c2976 | |
Hel Gibbons | c599ae4941 | |
Hel Gibbons | 0f5e96495c | |
Gee Bartlett | 9085c48a62 | |
Gee Bartlett | 7be5376abf | |
Gee Bartlett | 952be145ec | |
Gee Bartlett | 79eb998183 | |
helgibbons | 347cd19ab9 | |
helgibbons | 689326ac55 | |
helgibbons | cac74a94bb | |
helgibbons | 79a2349fac | |
helgibbons | 738531ebcf | |
Hel Gibbons | f592d2cd4d | |
lowfatcode | 92a48d20ae | |
lowfatcode | b0e194b2b1 | |
Brian Corteil | 4082d99186 | |
helgibbons | 987f98f7ca | |
Brian Corteil | 263c61c21a | |
Gee Bartlett | e85367a961 | |
Hel Gibbons | 9014c49ae1 | |
Mike Bell | b0d7f4c611 | |
Mike Bell | f76019f31c | |
Gee Bartlett | 982253c416 | |
Gee Bartlett | 9af1081e68 | |
Hel Gibbons | 69d5afb5c5 | |
ZodiusInfuser | f74261474e | |
ZodiusInfuser | 7ee40260c9 | |
Hel Gibbons | a5079f72f6 | |
ZodiusInfuser | 5a7939e95d | |
ZodiusInfuser | efba56b8b2 | |
ZodiusInfuser | 6e8f2d8a27 | |
ZodiusInfuser | 00d7b99589 | |
Hel Gibbons | 8ec96cee93 | |
Hel Gibbons | 152af1f72f | |
Hel Gibbons | 4e23ade374 | |
Hel Gibbons | 9bfa4b70b6 | |
Brian Corteil | c41b2b2109 | |
Brian Corteil | b035a1f007 | |
Hel Gibbons | db72276c1b | |
Hel Gibbons | d9587f06fa | |
Hel Gibbons | f678687735 | |
Hel Gibbons | 6a859134cf | |
Hel Gibbons | bafceb110a | |
Hel Gibbons | 23e73d043d | |
ZodiusInfuser | 27bf5549e6 | |
Hel Gibbons | be95e71049 | |
Hel Gibbons | 1ab922bb63 | |
Hel Gibbons | 64efea7ab5 | |
Hel Gibbons | 277080d786 | |
Hel Gibbons | 2dbf28fa9b | |
Hel Gibbons | 7fadbdc156 | |
Hel Gibbons | 21c32cfb34 | |
Hel Gibbons | 11a7be85a9 | |
Hel Gibbons | b1f6b00997 | |
Philip Howard | e928129d59 | |
Philip Howard | 3405130820 | |
helgibbons | f43293adf4 | |
helgibbons | 490d21f7e0 | |
helgibbons | 8f2837d930 | |
helgibbons | ce76ff5ea3 | |
ZodiusInfuser | a7dd4b3baf | |
ZodiusInfuser | 41225090ef | |
Phil Howard | d736342578 | |
helgibbons | 5d45f9aa06 | |
helgibbons | e986832414 | |
Phil Howard | 70b7d3065d | |
Phil Howard | a41715a377 | |
Phil Howard | 1357cb876d | |
ZodiusInfuser | 52a107e6d6 | |
ZodiusInfuser | df79caf34e | |
lowfatcode | 2695982182 | |
ZodiusInfuser | 8e8299a80b | |
jon | 27e9d467ff | |
ZodiusInfuser | 53dfb9866d | |
ZodiusInfuser | 882f76dcbc | |
ZodiusInfuser | deec835692 | |
ZodiusInfuser | 12ea527f44 | |
ZodiusInfuser | f809db6434 | |
ZodiusInfuser | e08ddd9837 | |
ZodiusInfuser | 4551c991fb | |
ZodiusInfuser | b2e4e16fab | |
ZodiusInfuser | 739ca71f69 | |
ZodiusInfuser | bb91d9e75b | |
ZodiusInfuser | fbc6737f1e | |
ZodiusInfuser | b35ed5d5ba | |
ZodiusInfuser | c8b5ffff8c | |
ZodiusInfuser | 2283e73368 | |
ZodiusInfuser | 34a1a54cd1 | |
ZodiusInfuser | 82b5110691 | |
ZodiusInfuser | 7fd175abc5 | |
ZodiusInfuser | a25699c73d | |
jon | 5ea389c12a | |
jon | 678be33b53 | |
jon | f2c3d15b8e | |
jon | eaaed2e862 | |
jon | 5fdd08438f | |
jon | a3d3707284 | |
jon | 09846ab221 | |
jon | 241be438b8 | |
jon | a322c400f0 | |
jon | c91c5d6bf4 | |
jon | a486150470 | |
jon | 63183d28be | |
jon | e50827c412 | |
jon | 210b334fff | |
jon | 54ad59c77a | |
helgibbons | d7f85c1160 | |
helgibbons | 8b19a9b314 | |
helgibbons | 76c6e36e59 | |
helgibbons | 6e93e4d5bf | |
helgibbons | bdc22af6e5 | |
helgibbons | 7014bcb749 | |
Gee Bartlett | 54018afde3 | |
helgibbons | db170a7b76 | |
helgibbons | 2c71351ef7 | |
helgibbons | 97abbb7af8 | |
Gee Bartlett | d03c00383a | |
Gee Bartlett | 0e5e7feb7d | |
Hel Gibbons | b22c536dcc | |
Hel Gibbons | f6cd2839cc | |
Gee Bartlett | fa04fef4f0 | |
Gee Bartlett | 337cd390d4 | |
Hel Gibbons | a27a539a1f | |
helgibbons | 86b1cd40dc | |
helgibbons | 262bd673ed | |
helgibbons | 3fa620db10 | |
Gee Bartlett | 2f7afad12d | |
Gee Bartlett | 28b6698430 | |
Philip Howard | 8280a6a720 | |
Philip Howard | 9878e4bff7 | |
Philip Howard | 1ac55c49ea | |
Philip Howard | 32ae781186 | |
Philip Howard | c3f3d7fa35 | |
helgibbons | 1886c4c3d2 | |
Gee Bartlett | 8f645257c3 | |
helgibbons | 283986b531 | |
helgibbons | 9653dcaabd | |
Phil Howard | 00ca53a30b | |
helgibbons | dd6d0a23ea | |
Phil Howard | eaf9fcdf93 | |
Phil Howard | 9663be2787 | |
Phil Howard | 9424ae7081 | |
Phil Howard | 14fec0406d | |
Hel Gibbons | 92ff903d1d | |
Hel Gibbons | 6393e74f86 | |
Peter Copeland | 8cc6e85f98 | |
Phil Howard | 6aa1bbd271 | |
Phil Howard | 7f02501fa6 | |
Gee Bartlett | 7b5946ef48 | |
Phil Howard | c1816ae9d6 | |
Gee Bartlett | c48f81bc90 | |
Gee Bartlett | 3f24b442f1 | |
Matt Gaunt-Seo | 4a645a1a68 | |
Hel Gibbons | fb8cde305d | |
Hel Gibbons | d6d1e00307 | |
Philip Howard | 3868e42b66 | |
Philip Howard | ad3cc3560a | |
Philip Howard | 92c8a650c5 | |
Phil Howard | fc3060b640 | |
Phil Howard | bb9b5b69d3 | |
Philip Howard | 675de25985 | |
Mike Bell | 799ed0786d | |
dreambucket13 | f427a74d55 | |
Philip Howard | c00fee673e | |
Tim Aidley | 213e47ffd7 | |
Tim Aidley | 6292d33cdb | |
Tim Aidley | f1aee84ef1 | |
helgibbons | dd872f5102 | |
helgibbons | b8110f3905 | |
helgibbons | 2cbd710620 | |
Mike Bell | 7f330d1a04 | |
Mike Bell | f341c15fa7 | |
Philip Howard | 05ef0d7f03 | |
Philip Howard | b8100d22e8 | |
Phil Howard | fddac54f86 | |
Philip Howard | 4e005c03d1 | |
btmask | 3cfac2787b | |
Phil Howard | 42a29dbe7b | |
Mike Bell | ac449ff640 | |
Phil Howard | f7781e2696 | |
Phil Howard | 898e2a1b32 | |
Phil Howard | 9ec77c2f10 | |
Phil Howard | fb246441bd | |
Phil Howard | a4b6d86ac7 | |
Phil Howard | dfa13f2a39 | |
Philip Howard | 82756a3fc9 | |
Philip Howard | 5b900cb088 | |
Philip Howard | 54ef2ddfd3 | |
Paul Beech | 553cedc80e | |
Phil Howard | d0dfa890df | |
Philip Howard | 832a8e261a | |
Mike Bell | b8ba66f593 | |
Phil Howard | d7894df308 | |
Philip Howard | 2855159921 | |
Phil Howard | b529475148 | |
Phil Howard | 527b97fb91 | |
Philip Howard | 4abe57d0f3 | |
Philip Howard | abbfb76eaa | |
Hel Gibbons | 41ea541b19 | |
jon | e9c18b109d | |
helgibbons | 822f3354aa | |
Philip Howard | e8e8ecdf5a | |
Phil Howard | 0734de30ea | |
ZodiusInfuser | 1cf7e29d09 | |
thinkofher | 2a9c2be115 |
|
@ -10,38 +10,37 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{matrix.name}}
|
||||
name: ${{matrix.name}} (C++)
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
name: Linux
|
||||
cache-key: linux
|
||||
cmake-args: '-DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DPICO_SDK_POST_LIST_DIRS=$GITHUB_WORKSPACE/pico-extras'
|
||||
apt-packages: ccache gcc-arm-none-eabi
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
- name: Pico
|
||||
board: pico
|
||||
- name: Pico W
|
||||
board: pico_w
|
||||
|
||||
env:
|
||||
PICO_SDK_PATH: $GITHUB_WORKSPACE/pico-sdk
|
||||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-cmake-${{github.ref}}-${{github.sha}}
|
||||
key: ccache-cmake-${{github.ref}}-${{matrix.board}}-${{github.sha}}
|
||||
restore-keys: |
|
||||
ccache-cmake-${{github.ref}}-${{matrix.board}}
|
||||
ccache-cmake-${{github.ref}}
|
||||
ccache-cmake
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
# Check out the Pico SDK
|
||||
- name: Checkout Pico SDK
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-sdk
|
||||
path: pico-sdk
|
||||
|
@ -49,7 +48,7 @@ jobs:
|
|||
|
||||
# Check out the Pico Extras
|
||||
- name: Checkout Pico Extras
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-extras
|
||||
path: pico-extras
|
||||
|
@ -59,7 +58,7 @@ jobs:
|
|||
- name: Install deps
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update && sudo apt install ${{matrix.apt-packages}}
|
||||
sudo apt update && sudo apt install ccache gcc-arm-none-eabi
|
||||
|
||||
- name: Create Build Environment
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
@ -67,7 +66,7 @@ jobs:
|
|||
- name: Configure CMake
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ${{matrix.cmake-args}}
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DPICO_SDK_POST_LIST_DIRS=$GITHUB_WORKSPACE/pico-extras -DPICO_BOARD=${{matrix.board}}
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
name: MicroPython for Badger2040
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
MICROPYTHON_VERSION: v1.19
|
||||
BOARD_TYPE: PIMORONI_BADGER2040
|
||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||
RELEASE_FILE: pimoroni-badger2040-${{github.event.release.tag_name || github.sha}}-micropython
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Dependencies
|
||||
steps:
|
||||
- name: Workspace Cache
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
|
||||
# Check out MicroPython
|
||||
- name: Checkout MicroPython
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
ref: ${{env.MICROPYTHON_VERSION}}
|
||||
submodules: false # MicroPython submodules are hideously broken
|
||||
path: micropython
|
||||
|
||||
- name: Fetch base MicroPython submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Fetch Pico SDK submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/lib/pico-sdk
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Build mpy-cross
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/mpy-cross
|
||||
run: make
|
||||
|
||||
build:
|
||||
needs: deps
|
||||
name: Build Badger 2040
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-micropython-badger2040-${{github.ref}}-${{github.sha}}
|
||||
restore-keys: |
|
||||
ccache-micropython-badger2040-${{github.ref}}
|
||||
ccache-micropython-badger2040-
|
||||
|
||||
- name: Workspace Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
path: pimoroni-pico-${{ github.sha }}
|
||||
|
||||
- name: "HACK: MicroPython Board Fixups"
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board/board-fixup.sh badger2040 ${{env.BOARD_TYPE}} ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board
|
||||
|
||||
# Linux deps
|
||||
- name: Install Compiler & CCache
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update && sudo apt install ccache gcc-arm-none-eabi
|
||||
python3 -m pip install pillow
|
||||
|
||||
# Build without BadgerOS
|
||||
- name: Configure MicroPython (No BadgerOS)
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{env.BOARD_TYPE}} -DBADGER2040_NO_MODULES=1 -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=../../../pimoroni-pico-${GITHUB_SHA}/micropython/modules/badger2040-micropython.cmake -DMICROPY_BOARD=${{env.BOARD_TYPE}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build MicroPython (No BadgerOS)
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-${{env.BOARD_TYPE}} -j 2
|
||||
ccache --show-stats || true
|
||||
|
||||
- name: Rename .uf2 for artifact (No BadgerOS)
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2/build-${{env.BOARD_TYPE}}
|
||||
run: |
|
||||
cp firmware.uf2 ${{env.RELEASE_FILE}}-without-badger-os.uf2
|
||||
|
||||
- name: Store .uf2 as artifact (No BadgerOS)
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}-without-badger-os.uf2
|
||||
path: micropython/ports/rp2/build-${{env.BOARD_TYPE}}/${{env.RELEASE_FILE}}-without-badger-os.uf2
|
||||
|
||||
- name: Upload .uf2 (No BadgerOS)
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
with:
|
||||
asset_path: micropython/ports/rp2/build-${{env.BOARD_TYPE}}/${{env.RELEASE_FILE}}-without-badger-os.uf2
|
||||
upload_url: ${{github.event.release.upload_url}}
|
||||
asset_name: ${{env.RELEASE_FILE}}-without-badger-os.uf2
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
# Build with BadgerOS
|
||||
- name: Configure MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{env.BOARD_TYPE}} -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=../../../pimoroni-pico-${GITHUB_SHA}/micropython/modules/badger2040-micropython.cmake -DMICROPY_BOARD=${{env.BOARD_TYPE}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-${{env.BOARD_TYPE}} -j 2
|
||||
ccache --show-stats || true
|
||||
|
||||
- name: Rename .uf2 for artifact
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2/build-${{env.BOARD_TYPE}}
|
||||
run: |
|
||||
cp firmware.uf2 ${{env.RELEASE_FILE}}.uf2
|
||||
|
||||
- name: Store .uf2 as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}.uf2
|
||||
path: micropython/ports/rp2/build-${{env.BOARD_TYPE}}/${{env.RELEASE_FILE}}.uf2
|
||||
|
||||
- name: Upload .uf2
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
with:
|
||||
asset_path: micropython/ports/rp2/build-${{env.BOARD_TYPE}}/${{env.RELEASE_FILE}}.uf2
|
||||
upload_url: ${{github.event.release.upload_url}}
|
||||
asset_name: ${{env.RELEASE_FILE}}.uf2
|
||||
asset_content_type: application/octet-stream
|
|
@ -1,152 +0,0 @@
|
|||
name: MicroPython
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
MICROPYTHON_VERSION: 45ab801c300d605db96229f6e0626ebe2801f24d
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Dependencies
|
||||
steps:
|
||||
- name: Workspace Cache
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
|
||||
# Check out MicroPython
|
||||
- name: Checkout MicroPython
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
ref: ${{env.MICROPYTHON_VERSION}}
|
||||
submodules: false # MicroPython submodules are hideously broken
|
||||
path: micropython
|
||||
|
||||
# Check out MicroPython Libs
|
||||
- name: Checkout MicroPython Libs
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: micropython/micropython-lib
|
||||
path: micropython-lib
|
||||
|
||||
- name: Fetch base MicroPython submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Fetch Pico SDK submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/lib/pico-sdk
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Build mpy-cross
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/mpy-cross
|
||||
run: make
|
||||
|
||||
build:
|
||||
needs: deps
|
||||
name: Build ${{matrix.board}}
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: picow
|
||||
board: PICO_W
|
||||
|
||||
env:
|
||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||
RELEASE_FILE: pimoroni-${{matrix.name}}-${{github.event.release.tag_name || github.sha}}-micropython
|
||||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-micropython-${{matrix.name}}-${{github.ref}}-${{github.sha}}
|
||||
restore-keys: |
|
||||
ccache-micropython-${{matrix.name}}-${{github.ref}}
|
||||
ccache-micropython-${{matrix.name}}-
|
||||
|
||||
- name: Workspace Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
|
||||
- name: Install Compiler & CCache
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update && sudo apt install ccache gcc-arm-none-eabi
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
path: pimoroni-pico-${{ github.sha }}
|
||||
|
||||
- name: "HACK: MicroPython Board Fixups"
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board/board-fixup.sh ${{matrix.name}} ${{matrix.board}} ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board
|
||||
|
||||
- name: Configure MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{matrix.board}} -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=../../../pimoroni-pico-${GITHUB_SHA}/micropython/modules/micropython-${{matrix.name}}.cmake -DMICROPY_BOARD=${{matrix.board}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-${{matrix.board}} -j 2
|
||||
ccache --show-stats || true
|
||||
|
||||
- name: Rename .uf2 for artifact
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2/build-${{matrix.board}}
|
||||
run: |
|
||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||
cp firmware.elf $RELEASE_FILE.elf
|
||||
|
||||
- name: Store .uf2 as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}.uf2
|
||||
path: micropython/ports/rp2/build-${{matrix.board}}/${{env.RELEASE_FILE}}.uf2
|
||||
|
||||
- name: Store .elf as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}.elf
|
||||
path: micropython/ports/rp2/build-${{matrix.board}}/${{env.RELEASE_FILE}}.elf
|
||||
|
||||
- name: Upload .uf2
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
with:
|
||||
asset_path: micropython/ports/rp2/build-${{matrix.board}}/firmware.uf2
|
||||
upload_url: ${{github.event.release.upload_url}}
|
||||
asset_name: ${{env.RELEASE_FILE}}.uf2
|
||||
asset_content_type: application/octet-stream
|
|
@ -7,60 +7,21 @@ on:
|
|||
types: [created]
|
||||
|
||||
env:
|
||||
MICROPYTHON_VERSION: v1.19
|
||||
MICROPYTHON_VERSION: v1.22.2
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Dependencies
|
||||
steps:
|
||||
- name: Workspace Cache
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
|
||||
# Check out MicroPython
|
||||
- name: Checkout MicroPython
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
ref: ${{env.MICROPYTHON_VERSION}}
|
||||
submodules: false # MicroPython submodules are hideously broken
|
||||
path: micropython
|
||||
|
||||
- name: Fetch base MicroPython submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Fetch Pico SDK submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/lib/pico-sdk
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Build mpy-cross
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/mpy-cross
|
||||
run: make
|
||||
|
||||
build:
|
||||
needs: deps
|
||||
name: Build ${{matrix.board}}
|
||||
name: ${{ matrix.name }} (${{ matrix.board }})
|
||||
runs-on: ubuntu-20.04
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: pico
|
||||
board: PICO
|
||||
- name: tiny2040
|
||||
board: RPI_PICO
|
||||
- name: picow
|
||||
board: RPI_PICO_W
|
||||
- name: tiny2040_8mb
|
||||
board: PIMORONI_TINY2040
|
||||
- name: picolipo_4mb
|
||||
board: PIMORONI_PICOLIPO_4MB
|
||||
|
@ -68,77 +29,113 @@ jobs:
|
|||
board: PIMORONI_PICOLIPO_16MB
|
||||
- name: tufty2040
|
||||
board: PIMORONI_TUFTY2040
|
||||
- name: enviro
|
||||
board: PICO_W_ENVIRO
|
||||
- name: galactic_unicorn
|
||||
board: RPI_PICO_W
|
||||
- name: cosmic_unicorn
|
||||
board: RPI_PICO_W
|
||||
- name: stellar_unicorn
|
||||
board: RPI_PICO_W
|
||||
- name: inky_frame
|
||||
board: PICO_W_INKY
|
||||
|
||||
env:
|
||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||
RELEASE_FILE: pimoroni-${{matrix.name}}-${{github.event.release.tag_name || github.sha}}-micropython.uf2
|
||||
RELEASE_FILE: pimoroni-${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-micropython
|
||||
PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}"
|
||||
MICROPY_BOARD_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}"
|
||||
USER_C_MODULES: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/modules/micropython-${{ matrix.name }}.cmake"
|
||||
TAG_OR_SHA: ${{ github.event.release.tag_name || github.sha }}
|
||||
MICROPY_BOARD: ${{ matrix.board }}
|
||||
BOARD_NAME: ${{ matrix.name }}
|
||||
BUILD_TOOLS: pimoroni-pico-${{ github.sha }}/ci/micropython.sh
|
||||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-micropython-${{matrix.name}}-${{github.ref}}-${{github.sha}}
|
||||
key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-micropython-${{matrix.name}}-${{github.ref}}
|
||||
ccache-micropython-${{matrix.name}}-
|
||||
|
||||
- name: Workspace Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
ccache-micropython-${{ matrix.name }}-${{ github.ref }}
|
||||
ccache-micropython-${{ matrix.name }}-
|
||||
|
||||
- name: Install Compiler & CCache
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update && sudo apt install ccache gcc-arm-none-eabi
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: pimoroni-pico-${{ github.sha }}
|
||||
|
||||
- name: "HACK: MicroPython Board Fixups"
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
|
||||
- name: Install Arm GNU Toolchain (arm-none-eabi-gcc)
|
||||
uses: carlosperate/arm-none-eabi-gcc-action@v1
|
||||
with:
|
||||
release: '9-2020-q2'
|
||||
|
||||
- name: Install CCache
|
||||
run: |
|
||||
../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board/board-fixup.sh ${{matrix.name}} ${{matrix.board}} ../../../pimoroni-pico-${GITHUB_SHA}/micropython/_board
|
||||
source $BUILD_TOOLS
|
||||
apt_install_build_deps
|
||||
|
||||
- name: Checkout MicroPython & Submodules
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
micropython_clone
|
||||
|
||||
- name: "Py_Decl: Checkout py_decl"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: gadgetoid/py_decl
|
||||
ref: v0.0.1
|
||||
path: py_decl
|
||||
|
||||
- name: Build MPY Cross
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
micropython_build_mpy_cross
|
||||
|
||||
- name: "HACK: CMakeLists.txt Disable C++ Exceptions Patch"
|
||||
shell: bash
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
hack_patch_micropython_disable_exceptions
|
||||
|
||||
- name: "HACK: Pico SDK Patch"
|
||||
shell: bash
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
hack_patch_pico_sdk
|
||||
|
||||
- name: Configure MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{matrix.board}} -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=../../../pimoroni-pico-${GITHUB_SHA}/micropython/modules/micropython-${{matrix.name}}.cmake -DMICROPY_BOARD=${{matrix.board}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
source $BUILD_TOOLS
|
||||
micropython_version
|
||||
cmake_configure
|
||||
|
||||
- name: Build MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-${{matrix.board}} -j 2
|
||||
ccache --show-stats || true
|
||||
source $BUILD_TOOLS
|
||||
cmake_build
|
||||
|
||||
- name: Rename .uf2 for artifact
|
||||
- name: "Py_Decl: Verify UF2"
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2/build-${{matrix.board}}
|
||||
run: cp firmware.uf2 $RELEASE_FILE
|
||||
run: |
|
||||
python3 py_decl/py_decl.py --to-json --verify build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||
|
||||
- name: Store .uf2 as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}
|
||||
path: micropython/ports/rp2/build-${{matrix.board}}/${{env.RELEASE_FILE}}
|
||||
name: ${{ env.RELEASE_FILE }}.uf2
|
||||
path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||
|
||||
- name: Upload .uf2
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_path: micropython/ports/rp2/build-${{matrix.board}}/firmware.uf2
|
||||
upload_url: ${{github.event.release.upload_url}}
|
||||
asset_name: ${{env.RELEASE_FILE}}
|
||||
asset_path: build-${{ matrix.name }}/firmware.uf2
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ env.RELEASE_FILE }}.uf2
|
||||
asset_content_type: application/octet-stream
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
name: Python Linting
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Python Deps
|
||||
run: python3 -m pip install flake8
|
||||
|
@ -17,14 +17,14 @@ jobs:
|
|||
- name: Lint micropython/modules_py
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -m flake8 --ignore E501 micropython/modules_py
|
||||
python3 -m flake8 --show-source --ignore E501 micropython/modules_py
|
||||
|
||||
- name: Lint micropython/examples
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -m flake8 --ignore E501 micropython/examples
|
||||
python3 -m flake8 --show-source --ignore E501 micropython/examples
|
||||
|
||||
- name: Lint .py tools in C++ examples
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -m flake8 --ignore E501 examples
|
||||
python3 -m flake8 --show-source --ignore E501 examples
|
|
@ -17,7 +17,11 @@
|
|||
[submodule "micropython/modules/qrcode"]
|
||||
path = micropython/modules/qrcode
|
||||
url = https://github.com/pimoroni/QR-Code-Generator
|
||||
branch = micropython/mp_obj_type_t_upgrade
|
||||
[submodule "drivers/vl53l5cx/src"]
|
||||
path = drivers/vl53l5cx/src
|
||||
url = https://github.com/ST-mirror/VL53L5CX_ULD_driver
|
||||
branch = no-fw/lite/en
|
||||
[submodule "drivers/mlx90640/src"]
|
||||
path = drivers/mlx90640/src
|
||||
url = https://github.com/melexis/mlx90640-library
|
||||
|
|
|
@ -10,6 +10,8 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
pico_find_compiler(PICO_COMPILER_LD ${PICO_GCC_TRIPLE}-ld)
|
||||
|
||||
function(add_resource target file)
|
||||
get_filename_component(NAME ${ARGV1} NAME_WE)
|
||||
set(FILENAME ${ARGV1})
|
||||
|
@ -21,7 +23,7 @@ function(add_resource target file)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
||||
COMMAND arm-none-eabi-ld -r -b binary -o ${NAME}.o ${FILENAME}
|
||||
COMMAND ${PICO_COMPILER_LD} -r -b binary -o ${NAME}.o ${FILENAME}
|
||||
DEPENDS ${FILENAME}
|
||||
)
|
||||
|
||||
|
|
56
README.md
56
README.md
|
@ -4,9 +4,9 @@ Welcome to the brave new world of Pico!
|
|||
|
||||
This repository contains the C/C++ and MicroPython libraries for our range of RP2040-based boards, Raspberry Pi Pico addons & [supported Breakout Garden sensors](#breakouts).
|
||||
|
||||
[![CMake Build Status](https://img.shields.io/github/workflow/status/pimoroni/pimoroni-pico/CMake?label=C%2B%2B)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/cmake.yml)
|
||||
[![MicroPython Build Status](https://img.shields.io/github/workflow/status/pimoroni/pimoroni-pico/MicroPython?label=MicroPython)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/micropython.yml)
|
||||
[![MicroPython+Blinka Status](https://img.shields.io/github/workflow/status/pimoroni/pimoroni-pico/MicroPython+Blinka?label=MicroPython%2BBlinka)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/micropython-with-blinka.yml)
|
||||
[![CMake Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pimoroni-pico/cmake.yml?branch=main&label=CMake)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/cmake.yml)
|
||||
[![MicroPython Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pimoroni-pico/micropython.yml?branch=main&label=MicroPython)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/micropython.yml)
|
||||
[![MicroPython PicoW Build Status](https://img.shields.io/github/actions/workflow/status/pimoroni/pimoroni-pico/micropython-picow.yml?branch=main&label=MicroPython%20PicoW)](https://github.com/pimoroni/pimoroni-pico/actions/workflows/micropython-picow.yml)
|
||||
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/pimoroni/pimoroni-pico)](https://github.com/pimoroni/pimoroni-pico/releases/latest/)
|
||||
|
||||
|
||||
|
@ -16,7 +16,13 @@ This repository contains the C/C++ and MicroPython libraries for our range of RP
|
|||
- [C++ Examples](#c-examples)
|
||||
- [Boilerplate for C++ Projects](#boilerplate-for-c-projects)
|
||||
- [Supported Products](#supported-products)
|
||||
- [Tutorials & Guides](#tutorials--guides)
|
||||
- [Packs and Bases](#packs-and-bases)
|
||||
- [SHIMs](#shims)
|
||||
- [RP2040 Boards](#rp2040-boards)
|
||||
- [Pico W Aboard](#pico-w-aboard)
|
||||
- [Breakouts](#breakouts)
|
||||
- [Kits](#kits)
|
||||
- [Tutorials and Guides](#tutorials-and-guides)
|
||||
|
||||
# MicroPython
|
||||
|
||||
|
@ -26,8 +32,9 @@ The easiest way to get started. If you're new to Pico, we recommend you read our
|
|||
|
||||
New releases are issued regularly with new libraries, bug fixes to our existing libraries and new features inherited from MicroPython upstream. Be sure to check back!
|
||||
|
||||
* :link: [Tutorial: Getting started with Pico](https://learn.pimoroni.com/article/getting-started-with-pico)
|
||||
* [Readme: Instructions for setting up MicroPython](setting-up-micropython.md)
|
||||
* :link: [Learn: Getting started with Pico](https://learn.pimoroni.com/article/getting-started-with-pico)
|
||||
* [Readme: Instructions for installing MicroPython](setting-up-micropython.md)
|
||||
* [Readme: Frequently Asked Questions](faqs-micropython.md)
|
||||
* [Pimoroni Pico MicroPython + Drivers Releases](https://github.com/pimoroni/pimoroni-pico/releases)
|
||||
* [Readme: PicoGraphics](micropython/modules/picographics)
|
||||
|
||||
|
@ -37,11 +44,15 @@ You can find MicroPython examples for supported sensors, packs and bases in the
|
|||
|
||||
* [MicroPython Examples](micropython/examples)
|
||||
|
||||
You can also install MicroPython stubs into Visual Studio Code to give you auto-complete, see:
|
||||
|
||||
* [MicroPython Stubs](https://github.com/pimoroni/pimoroni-pico-stubs)
|
||||
|
||||
# C/C++
|
||||
|
||||
For more advanced users that want to unleash the full power of Pico, you can use our C++ libraries. If you know what you're doing and want to build your own Pimoroni Pico project then start with the [Pimoroni Pico SDK Boilerplate](https://github.com/pimoroni/pico-boilerplate).
|
||||
Advanced users that want to unleash the full power of Pico can use our C++ libraries. If you know what you're doing and want to build your own Pimoroni Pico project then start with the [Pimoroni Pico SDK Boilerplate](https://github.com/pimoroni/pico-boilerplate).
|
||||
|
||||
* :link: [Tutorial: Pico C++ Development on Windows](https://learn.pimoroni.com/article/pico-development-using-wsl)
|
||||
* :link: [Learn: Pico C++ Development on Windows](https://learn.pimoroni.com/article/pico-development-using-wsl)
|
||||
* [Readme: Instructions for setting up the C/C++ SDK](setting-up-the-pico-sdk.md)
|
||||
|
||||
## C++ Examples
|
||||
|
@ -72,6 +83,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
* Pico Display 2.0 - https://shop.pimoroni.com/products/pico-display-pack-2-0
|
||||
* Pico Enviro+ Pack - https://shop.pimoroni.com/products/pico-enviro-pack
|
||||
* Pico Inky Pack - https://shop.pimoroni.com/products/pico-inky-pack
|
||||
* Pico GFX Pack - https://shop.pimoroni.com/products/pico-gfx-pack
|
||||
|
||||
## SHIMs
|
||||
|
||||
|
@ -81,7 +93,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
## RP2040 Boards
|
||||
|
||||
* Plasma 2040 (LED strip driver) - https://shop.pimoroni.com/products/plasma-2040
|
||||
* Interstate 75 (HUB75 driver) - https://shop.pimoroni.com/products/interstate-75
|
||||
* Interstate 75 (HUB75 matrix driver) - https://shop.pimoroni.com/products/interstate-75
|
||||
* Badger 2040 (E Ink badge) - https://shop.pimoroni.com/products/badger-2040
|
||||
* Servo 2040 (18 Channel Servo Controller) - https://shop.pimoroni.com/products/servo-2040
|
||||
* Motor 2040 (Quad Motor+Encoder Controller) - https://shop.pimoroni.com/products/motor-2040
|
||||
|
@ -91,6 +103,15 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
|
||||
* Automation 2040 W (inputs, outputs and relays, 6-40V compatible) - https://shop.pimoroni.com/products/automation-2040-w
|
||||
* Inventor 2040 W (motors, servos, noise) - https://shop.pimoroni.com/products/inventor-2040-w
|
||||
* Inky Frame 5.7" (7-colour E Ink) - https://shop.pimoroni.com/products/inky-frame-5-7
|
||||
* Automation 2040 W Mini (inputs, outputs and a relay, 6-40V compatible) - https://shop.pimoroni.com/products/automation-2040-w-mini
|
||||
* Plasma Stick 2040 W (bijou LED strip controller) - https://shop.pimoroni.com/products/plasma-stick-2040-w
|
||||
* Galactic Unicorn (53 x 11 LED matrix) - https://shop.pimoroni.com/products/galactic-unicorn
|
||||
* Interstate 75 W (HUB75 matrix driver) - https://shop.pimoroni.com/products/interstate-75-w
|
||||
* Inky Frame 4.0" (7-colour E Ink) - https://shop.pimoroni.com/products/inky-frame-4
|
||||
* Badger 2040 W (E Ink badge) - https://shop.pimoroni.com/products/badger-2040-w
|
||||
* Cosmic Unicorn (32 x 32 LED matrix) - https://shop.pimoroni.com/products/cosmic-unicorn
|
||||
* Inky Frame 7.3" (7-colour E Ink) - https://shop.pimoroni.com/products/inky-frame-7-3
|
||||
|
||||
## Breakouts
|
||||
|
||||
|
@ -99,6 +120,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
* MICS6814 - Gas Sensor - https://shop.pimoroni.com/products/mics6814-gas-sensor-breakout
|
||||
* RGB Potentiometer - https://shop.pimoroni.com/products/rgb-potentiometer-breakout
|
||||
* RGB Encoder - https://shop.pimoroni.com/products/rgb-encoder-breakout
|
||||
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
|
||||
* IO Expander - https://shop.pimoroni.com/products/io-expander
|
||||
* RV3028 - Real-Time Clock (RTC) - https://shop.pimoroni.com/products/rv3028-real-time-clock-rtc-breakout
|
||||
* ST7735 - 0.96" LCD - https://shop.pimoroni.com/products/0-96-spi-colour-lcd-160x80-breakout
|
||||
|
@ -122,15 +144,23 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
* ICP10125 - High Accuracy Pressure / Altitude / Temperature Sensor - https://shop.pimoroni.com/products/icp10125-air-pressure-breakout
|
||||
* SCD41 CO2 Sensor (Carbon Dioxide / Temperature / Humidity) - https://shop.pimoroni.com/products/scd41-co2-sensor-breakout
|
||||
* VL53L5CX 8x8 Time of Flight Array Sensor - https://shop.pimoroni.com/products/vl53l5cx-time-of-flight-tof-sensor-breakout
|
||||
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
|
||||
|
||||
## Kits
|
||||
|
||||
# Tutorials & Guides
|
||||
* Wireless Plasma Kit (Plasma Stick + LED wire + bottle!) - https://shop.pimoroni.com/products/wireless-plasma-kit
|
||||
|
||||
- :link: [Getting started with (MicroPython on) Pico](https://learn.pimoroni.com/article/getting-started-with-pico)
|
||||
- :link: [Pico C++ Development on Windows / WSL](https://learn.pimoroni.com/article/pico-development-using-wsl)
|
||||
- :link: [Getting Started with Interstate 75](https://learn.pimoroni.com/article/getting-started-with-interstate-75)
|
||||
# Tutorials and Guides
|
||||
|
||||
- :link: [Getting Started with (MicroPython on) Pico](https://learn.pimoroni.com/article/getting-started-with-pico)
|
||||
- :link: [Pico C/C++ Development on Windows / WSL](https://learn.pimoroni.com/article/pico-development-using-wsl)
|
||||
- :link: [Getting Started with Interstate 75 (and W)](https://learn.pimoroni.com/article/getting-started-with-interstate-75)
|
||||
- :link: [Getting Started with Plasma 2040](https://learn.pimoroni.com/article/plasma-2040)
|
||||
- :link: [Assembling Keybow 2040](https://learn.pimoroni.com/article/assembling-keybow-2040)
|
||||
- :link: [Getting Started with Badger 2040](https://learn.pimoroni.com/article/getting-started-with-badger-2040)
|
||||
- :link: [MicroPython and VL53L5CX](https://learn.pimoroni.com/article/micropython-and-vl53l5cx)
|
||||
- :link: [Getting Started with Tufty 2040](https://learn.pimoroni.com/article/getting-started-with-tufty-2040)
|
||||
- :link: [Getting Started with Inky Frame](https://learn.pimoroni.com/article/getting-started-with-inky-frame)
|
||||
- :link: [Getting Started with Automation 2040 W (and Mini)](https://learn.pimoroni.com/article/getting-started-with-automation-2040-w)
|
||||
- :link: [Assembling Wireless Plasma Kit](https://learn.pimoroni.com/article/assembling-wireless-plasma-kit)
|
||||
- :link: [Getting Started with Badger 2040 W](https://learn.pimoroni.com/article/getting-started-with-badger-2040-w)
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
export TERM=${TERM:="xterm-256color"}
|
||||
|
||||
function log_success {
|
||||
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function log_inform {
|
||||
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function log_warning {
|
||||
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function micropython_clone {
|
||||
log_inform "Using MicroPython $MICROPYTHON_VERSION"
|
||||
git clone https://github.com/micropython/micropython --depth=1 --branch=$MICROPYTHON_VERSION
|
||||
cd micropython
|
||||
git submodule update --init lib/pico-sdk
|
||||
git submodule update --init lib/cyw43-driver
|
||||
git submodule update --init lib/lwip
|
||||
git submodule update --init lib/mbedtls
|
||||
git submodule update --init lib/micropython-lib
|
||||
git submodule update --init lib/tinyusb
|
||||
git submodule update --init lib/btstack
|
||||
cd ../
|
||||
}
|
||||
|
||||
function micropython_build_mpy_cross {
|
||||
cd micropython/mpy-cross
|
||||
ccache --zero-stats || true
|
||||
CROSS_COMPILE="ccache " make
|
||||
ccache --show-stats || true
|
||||
cd ../../
|
||||
}
|
||||
|
||||
function apt_install_build_deps {
|
||||
sudo apt update && sudo apt install ccache
|
||||
}
|
||||
|
||||
function micropython_version {
|
||||
echo "MICROPY_GIT_TAG=$MICROPYTHON_VERSION, $BOARD_NAME $TAG_OR_SHA" >> $GITHUB_ENV
|
||||
echo "MICROPY_GIT_HASH=$MICROPYTHON_VERSION-$TAG_OR_SHA" >> $GITHUB_ENV
|
||||
}
|
||||
|
||||
function hack_patch_micropython_disable_exceptions {
|
||||
cd micropython
|
||||
git apply $PIMORONI_PICO_DIR/micropython/micropython_nano_specs.patch
|
||||
cd ../
|
||||
}
|
||||
|
||||
function hack_patch_pico_sdk {
|
||||
# pico-sdk-patch.sh will apply the patch if it exists
|
||||
cd micropython
|
||||
$PIMORONI_PICO_DIR/micropython/board/pico-sdk-patch.sh $MICROPY_BOARD
|
||||
cd ../
|
||||
}
|
||||
|
||||
function cmake_configure {
|
||||
cmake -S micropython/ports/rp2 -B build-$BOARD_NAME \
|
||||
-DPICO_BUILD_DOCS=0 \
|
||||
-DUSER_C_MODULES=$USER_C_MODULES \
|
||||
-DMICROPY_BOARD_DIR=$MICROPY_BOARD_DIR \
|
||||
-DMICROPY_BOARD=$MICROPY_BOARD \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
}
|
||||
|
||||
function cmake_build {
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-$BOARD_NAME -j 2
|
||||
ccache --show-stats || true
|
||||
cd build-$BOARD_NAME
|
||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||
}
|
Binary file not shown.
|
@ -50,9 +50,11 @@ namespace pimoroni {
|
|||
enum BOARD {
|
||||
BREAKOUT_GARDEN,
|
||||
PICO_EXPLORER,
|
||||
PLASMA_STICK,
|
||||
PLASMA_2040,
|
||||
INTERSTATE_75,
|
||||
SERVO_2040
|
||||
SERVO_2040,
|
||||
MOTOR_2040
|
||||
};
|
||||
|
||||
enum Rotation {
|
||||
|
@ -76,7 +78,7 @@ namespace pimoroni {
|
|||
return to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
constexpr uint8_t GAMMA_8BIT[256] = {
|
||||
inline constexpr uint8_t GAMMA_8BIT[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
|
||||
2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
|
||||
|
@ -96,7 +98,7 @@ namespace pimoroni {
|
|||
|
||||
/* Moved from pico_unicorn.cpp
|
||||
v = (uint16_t)(powf((float)(n) / 255.0f, 2.2) * 16383.0f + 0.5f) */
|
||||
constexpr uint16_t GAMMA_14BIT[256] = {
|
||||
inline constexpr uint16_t GAMMA_14BIT[256] = {
|
||||
0, 0, 0, 1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 23, 28, 32,
|
||||
37, 42, 48, 54, 61, 67, 75, 82, 90, 99, 108, 117, 127, 137, 148, 159,
|
||||
170, 182, 195, 207, 221, 234, 249, 263, 278, 294, 310, 326, 343, 361, 379, 397,
|
||||
|
|
|
@ -29,9 +29,15 @@ namespace pimoroni {
|
|||
scl = I2C_DEFAULT_SCL;
|
||||
interrupt = I2C_DEFAULT_INT;
|
||||
break;
|
||||
case PLASMA_STICK:
|
||||
sda = I2C_BG_SDA;
|
||||
scl = I2C_BG_SCL;
|
||||
interrupt = PIN_UNUSED;
|
||||
break;
|
||||
case PLASMA_2040:
|
||||
case INTERSTATE_75:
|
||||
case SERVO_2040:
|
||||
case MOTOR_2040:
|
||||
sda = I2C_HEADER_SDA;
|
||||
scl = I2C_HEADER_SCL;
|
||||
interrupt = I2C_HEADER_INT;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
|
@ -27,6 +27,7 @@ add_subdirectory(rgbled)
|
|||
add_subdirectory(icp10125)
|
||||
add_subdirectory(scd4x)
|
||||
add_subdirectory(hub75)
|
||||
add_subdirectory(hub75_legacy)
|
||||
add_subdirectory(uc8151)
|
||||
add_subdirectory(uc8159)
|
||||
add_subdirectory(uc8151_legacy)
|
||||
|
@ -38,3 +39,8 @@ add_subdirectory(vl53l5cx)
|
|||
add_subdirectory(pcf85063a)
|
||||
add_subdirectory(pms5003)
|
||||
add_subdirectory(sh1107)
|
||||
add_subdirectory(st7567)
|
||||
add_subdirectory(psram_display)
|
||||
add_subdirectory(shiftregister)
|
||||
add_subdirectory(inky73)
|
||||
add_subdirectory(mlx90640)
|
|
@ -11,7 +11,7 @@ namespace pimoroni {
|
|||
public:
|
||||
Analog(uint pin, float amplifier_gain = 1.0f, float resistor = 0.0f, float offset = 0.0f) :
|
||||
pin(pin), amplifier_gain(amplifier_gain), resistor(resistor), offset(offset) {
|
||||
adc_init();
|
||||
if (!(adc_hw->cs & ADC_CS_EN_BITS)) adc_init();
|
||||
|
||||
//Make sure GPIO is high-impedance, no pullups etc
|
||||
adc_gpio_init(pin);
|
||||
|
|
|
@ -11,7 +11,10 @@ namespace pimoroni {
|
|||
gpio_pull_up(interrupt);
|
||||
}
|
||||
|
||||
device.intf_ptr = new i2c_intf_ptr{.i2c = i2c, .address = address};
|
||||
i2c_interface.i2c = i2c;
|
||||
i2c_interface.address = address;
|
||||
|
||||
device.intf_ptr = &i2c_interface;
|
||||
device.intf = bme280_intf::BME280_I2C_INTF;
|
||||
device.read = (bme280_read_fptr_t)&read_bytes;
|
||||
device.write = (bme280_write_fptr_t)&write_bytes;
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace pimoroni {
|
|||
bool status;
|
||||
};
|
||||
|
||||
i2c_intf_ptr i2c_interface;
|
||||
|
||||
bool debug = false;
|
||||
|
||||
bool init();
|
||||
|
|
|
@ -11,8 +11,10 @@ namespace pimoroni {
|
|||
gpio_pull_up(interrupt);
|
||||
}
|
||||
|
||||
device.intf_ptr = new i2c_intf_ptr{.i2c = i2c, .address = address};
|
||||
i2c_interface.i2c = i2c;
|
||||
i2c_interface.address = address;
|
||||
|
||||
device.intf_ptr = &i2c_interface;
|
||||
device.intf = bme68x_intf::BME68X_I2C_INTF;
|
||||
device.read = (bme68x_read_fptr_t)&read_bytes;
|
||||
device.write = (bme68x_write_fptr_t)&write_bytes;
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace pimoroni {
|
|||
int8_t address;
|
||||
};
|
||||
|
||||
i2c_intf_ptr i2c_interface;
|
||||
|
||||
bool debug = true;
|
||||
|
||||
bool init();
|
||||
|
|
|
@ -11,7 +11,10 @@ namespace pimoroni {
|
|||
gpio_pull_up(interrupt);
|
||||
}
|
||||
|
||||
device.intf_ptr = new i2c_intf_ptr{.i2c = i2c, .address = address};
|
||||
i2c_interface.i2c = i2c;
|
||||
i2c_interface.address = address;
|
||||
|
||||
device.intf_ptr = &i2c_interface;
|
||||
device.intf = BMP280_I2C_INTF;
|
||||
device.read = (bmp280_com_fptr_t)&read_bytes;
|
||||
device.write = (bmp280_com_fptr_t)&write_bytes;
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace pimoroni {
|
|||
bool status;
|
||||
};
|
||||
|
||||
i2c_intf_ptr i2c_interface;
|
||||
|
||||
bool debug = false;
|
||||
|
||||
bool init();
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
#include "hardware/irq.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "encoder.hpp"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "encoder.pio.h"
|
||||
#endif
|
||||
|
||||
#define LAST_STATE(state) ((state) & 0b0011)
|
||||
#define CURR_STATE(state) (((state) & 0b1100) >> 2)
|
||||
|
|
|
@ -3,9 +3,10 @@ add_library(hub75 INTERFACE)
|
|||
target_sources(hub75 INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/hub75.cpp)
|
||||
|
||||
pico_generate_pio_header(hub75 ${CMAKE_CURRENT_LIST_DIR}/hub75.pio)
|
||||
|
||||
target_include_directories(hub75 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(hub75 INTERFACE pico_stdlib hardware_pio hardware_dma)
|
||||
target_link_libraries(hub75 INTERFACE pico_stdlib hardware_pio hardware_dma pico_graphics)
|
||||
|
||||
pico_generate_pio_header(hub75 ${CMAKE_CURRENT_LIST_DIR}/hub75.pio)
|
|
@ -4,40 +4,10 @@
|
|||
|
||||
#include "hub75.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
// Basic function to convert Hue, Saturation and Value to an RGB colour
|
||||
Pixel hsv_to_rgb(float h, float s, float v) {
|
||||
if(h < 0.0f) {
|
||||
h = 1.0f + fmod(h, 1.0f);
|
||||
}
|
||||
|
||||
int i = int(h * 6);
|
||||
float f = h * 6 - i;
|
||||
|
||||
v = v * 255.0f;
|
||||
|
||||
float sv = s * v;
|
||||
float fsv = f * sv;
|
||||
|
||||
auto p = uint8_t(-sv + v);
|
||||
auto q = uint8_t(-fsv + v);
|
||||
auto t = uint8_t(fsv - sv + v);
|
||||
|
||||
uint8_t bv = uint8_t(v);
|
||||
|
||||
switch (i % 6) {
|
||||
default:
|
||||
case 0: return Pixel(bv, t, p);
|
||||
case 1: return Pixel(q, bv, p);
|
||||
case 2: return Pixel(p, bv, t);
|
||||
case 3: return Pixel(p, q, bv);
|
||||
case 4: return Pixel(t, p, bv);
|
||||
case 5: return Pixel(bv, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb)
|
||||
: width(width), height(height), panel_type(panel_type), inverted_stb(inverted_stb)
|
||||
Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb, COLOR_ORDER color_order)
|
||||
: width(width), height(height), panel_type(panel_type), inverted_stb(inverted_stb), color_order(color_order)
|
||||
{
|
||||
// Set up allllll the GPIO
|
||||
gpio_init(pin_r0); gpio_set_function(pin_r0, GPIO_FUNC_SIO); gpio_set_dir(pin_r0, true); gpio_put(pin_r0, 0);
|
||||
|
@ -59,12 +29,10 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool
|
|||
gpio_init(pin_oe); gpio_set_function(pin_oe, GPIO_FUNC_SIO); gpio_set_dir(pin_oe, true); gpio_put(pin_clk, !oe_polarity);
|
||||
|
||||
if (buffer == nullptr) {
|
||||
front_buffer = new Pixel[width * height];
|
||||
back_buffer = new Pixel[width * height];
|
||||
managed_buffer = true;
|
||||
} else {
|
||||
front_buffer = buffer;
|
||||
back_buffer = buffer + width * height;
|
||||
back_buffer = buffer;
|
||||
managed_buffer = false;
|
||||
}
|
||||
|
||||
|
@ -86,15 +54,30 @@ void Hub75::set_color(uint x, uint y, Pixel c) {
|
|||
} else {
|
||||
offset = (y * width + x) * 2;
|
||||
}
|
||||
front_buffer[offset] = c;
|
||||
back_buffer[offset] = c;
|
||||
}
|
||||
|
||||
void Hub75::set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
set_color(x, y, Pixel(r, g, b));
|
||||
}
|
||||
|
||||
void Hub75::set_hsv(uint x, uint y, float h, float s, float v) {
|
||||
set_color(x, y, hsv_to_rgb(h, s, v));
|
||||
void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
switch(color_order) {
|
||||
case COLOR_ORDER::RGB:
|
||||
set_color(x, y, Pixel(r, g, b));
|
||||
break;
|
||||
case COLOR_ORDER::RBG:
|
||||
set_color(x, y, Pixel(r, b, g));
|
||||
break;
|
||||
case COLOR_ORDER::GRB:
|
||||
set_color(x, y, Pixel(g, r, b));
|
||||
break;
|
||||
case COLOR_ORDER::GBR:
|
||||
set_color(x, y, Pixel(g, b, r));
|
||||
break;
|
||||
case COLOR_ORDER::BRG:
|
||||
set_color(x, y, Pixel(b, r, g));
|
||||
break;
|
||||
case COLOR_ORDER::BGR:
|
||||
set_color(x, y, Pixel(b, g, r));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) {
|
||||
|
@ -130,12 +113,6 @@ void Hub75::FM6126A_setup() {
|
|||
|
||||
void Hub75::start(irq_handler_t handler) {
|
||||
if(handler) {
|
||||
dma_channel = 0;
|
||||
|
||||
// Try as I might, I can't seem to coax MicroPython into leaving PIO in a known state upon soft reset
|
||||
// check for claimed PIO and prepare a clean slate.
|
||||
stop(handler);
|
||||
|
||||
if (panel_type == PANEL_FM6126A) {
|
||||
FM6126A_setup();
|
||||
}
|
||||
|
@ -156,31 +133,21 @@ void Hub75::start(irq_handler_t handler) {
|
|||
// Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly
|
||||
pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f);
|
||||
|
||||
dma_channel_claim(dma_channel);
|
||||
dma_channel = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
||||
channel_config_set_bswap(&config, false);
|
||||
channel_config_set_dreq(&config, pio_get_dreq(pio, sm_data, true));
|
||||
dma_channel_configure(dma_channel, &config, &pio->txf[sm_data], NULL, 0, false);
|
||||
|
||||
dma_channel_claim(dma_flip_channel);
|
||||
dma_channel_config flip_config = dma_channel_get_default_config(dma_flip_channel);
|
||||
channel_config_set_transfer_data_size(&flip_config, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&flip_config, true);
|
||||
channel_config_set_write_increment(&flip_config, true);
|
||||
channel_config_set_bswap(&flip_config, false);
|
||||
dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false);
|
||||
|
||||
// Same handler for both DMA channels
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, handler);
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, handler);
|
||||
irq_add_shared_handler(DMA_IRQ_0, handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
|
||||
dma_channel_set_irq1_enabled(dma_flip_channel, true);
|
||||
dma_channel_set_irq0_enabled(dma_channel, true);
|
||||
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), true);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
irq_set_enabled(DMA_IRQ_1, true);
|
||||
|
||||
row = 0;
|
||||
bit = 0;
|
||||
|
@ -192,13 +159,11 @@ void Hub75::start(irq_handler_t handler) {
|
|||
}
|
||||
|
||||
void Hub75::stop(irq_handler_t handler) {
|
||||
do_flip = false;
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
irq_set_enabled(DMA_IRQ_1, false);
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), false);
|
||||
|
||||
if(dma_channel_is_claimed(dma_channel)) {
|
||||
if(dma_channel != -1 && dma_channel_is_claimed(dma_channel)) {
|
||||
dma_channel_set_irq0_enabled(dma_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_0, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_channel);
|
||||
|
@ -207,29 +172,24 @@ void Hub75::stop(irq_handler_t handler) {
|
|||
dma_channel_unclaim(dma_channel);
|
||||
}
|
||||
|
||||
if(dma_channel_is_claimed(dma_flip_channel)){
|
||||
dma_channel_set_irq1_enabled(dma_flip_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_1, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_flip_channel);
|
||||
dma_channel_abort(dma_flip_channel);
|
||||
dma_channel_acknowledge_irq1(dma_flip_channel);
|
||||
dma_channel_unclaim(dma_flip_channel);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_data)) {
|
||||
pio_sm_set_enabled(pio, sm_data, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_data);
|
||||
pio_remove_program(pio, &hub75_data_rgb888_program, data_prog_offs);
|
||||
pio_sm_unclaim(pio, sm_data);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_row)) {
|
||||
pio_sm_set_enabled(pio, sm_row, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_row);
|
||||
if (inverted_stb) {
|
||||
pio_remove_program(pio, &hub75_row_inverted_program, row_prog_offs);
|
||||
} else {
|
||||
pio_remove_program(pio, &hub75_row_program, row_prog_offs);
|
||||
}
|
||||
pio_sm_unclaim(pio, sm_row);
|
||||
}
|
||||
|
||||
pio_clear_instruction_memory(pio);
|
||||
|
||||
// Make sure the GPIO is in a known good state
|
||||
// since we don't know what the PIO might have done with it
|
||||
gpio_put_masked(0b111111 << pin_r0, 0);
|
||||
|
@ -240,7 +200,6 @@ void Hub75::stop(irq_handler_t handler) {
|
|||
|
||||
Hub75::~Hub75() {
|
||||
if (managed_buffer) {
|
||||
delete[] front_buffer;
|
||||
delete[] back_buffer;
|
||||
}
|
||||
}
|
||||
|
@ -248,31 +207,13 @@ Hub75::~Hub75() {
|
|||
void Hub75::clear() {
|
||||
for(auto x = 0u; x < width; x++) {
|
||||
for(auto y = 0u; y < height; y++) {
|
||||
set_rgb(x, y, 0, 0, 0);
|
||||
set_pixel(x, y, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::flip(bool copybuffer) {
|
||||
dma_channel_config flip_config = dma_get_channel_config(dma_flip_channel);
|
||||
channel_config_set_read_increment(&flip_config, copybuffer);
|
||||
dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false);
|
||||
|
||||
dma_channel_set_read_addr(dma_flip_channel, copybuffer ? front_buffer : &background, false);
|
||||
dma_channel_set_write_addr(dma_flip_channel, back_buffer, false);
|
||||
|
||||
// Flip and block until the front buffer has been prepared
|
||||
do_flip = true;
|
||||
while(do_flip) {
|
||||
best_effort_wfe_or_timeout(make_timeout_time_us(10));
|
||||
};
|
||||
}
|
||||
|
||||
void Hub75::dma_complete() {
|
||||
if(dma_channel_get_irq1_status(dma_flip_channel)) {
|
||||
dma_channel_acknowledge_irq1(dma_flip_channel);
|
||||
do_flip = false;
|
||||
}
|
||||
|
||||
if(dma_channel_get_irq0_status(dma_channel)) {
|
||||
dma_channel_acknowledge_irq0(dma_channel);
|
||||
|
@ -290,15 +231,6 @@ void Hub75::dma_complete() {
|
|||
// Latch row data, pulse output enable for new row.
|
||||
pio_sm_put_blocking(pio, sm_row, row | (brightness << 5 << bit));
|
||||
|
||||
if (do_flip && bit == 0 && row == 0) {
|
||||
// Literally flip the front and back buffers by swapping their addresses
|
||||
Pixel *tmp = back_buffer;
|
||||
back_buffer = front_buffer;
|
||||
front_buffer = tmp;
|
||||
// Then, read the contents of the back buffer into the front buffer
|
||||
dma_channel_set_trans_count(dma_flip_channel, width * height, true);
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
if(row == height / 2) {
|
||||
|
@ -313,4 +245,34 @@ void Hub75::dma_complete() {
|
|||
dma_channel_set_trans_count(dma_channel, width * 2, false);
|
||||
dma_channel_set_read_addr(dma_channel, &back_buffer[row * width * 2], true);
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::update(PicoGraphics *graphics) {
|
||||
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
|
||||
uint32_t *p = (uint32_t *)graphics->frame_buffer;
|
||||
for(uint y = 0; y < height; y++) {
|
||||
for(uint x = 0; x < width; x++) {
|
||||
uint32_t col = *p;
|
||||
uint8_t r = (col & 0xff0000) >> 16;
|
||||
uint8_t g = (col & 0x00ff00) >> 8;
|
||||
uint8_t b = (col & 0x0000ff) >> 0;
|
||||
set_pixel(x, y, r, g, b);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
|
||||
uint16_t *p = (uint16_t *)graphics->frame_buffer;
|
||||
for(uint y = 0; y < height; y++) {
|
||||
for(uint x = 0; x < width; x++) {
|
||||
uint16_t col = __builtin_bswap16(*p);
|
||||
uint8_t r = (col & 0b1111100000000000) >> 8;
|
||||
uint8_t g = (col & 0b0000011111100000) >> 3;
|
||||
uint8_t b = (col & 0b0000000000011111) << 3;
|
||||
set_pixel(x, y, r, g, b);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,8 +4,13 @@
|
|||
#include "hardware/pio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hub75.pio.h"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "hub75.pio.h"
|
||||
#endif
|
||||
|
||||
namespace pimoroni {
|
||||
const uint DATA_BASE_PIN = 0;
|
||||
const uint DATA_N_PINS = 6;
|
||||
const uint ROWSEL_BASE_PIN = 6;
|
||||
|
@ -50,19 +55,25 @@ Pixel hsv_to_rgb(float h, float s, float v);
|
|||
|
||||
class Hub75 {
|
||||
public:
|
||||
enum class COLOR_ORDER {
|
||||
RGB,
|
||||
RBG,
|
||||
GRB,
|
||||
GBR,
|
||||
BRG,
|
||||
BGR
|
||||
};
|
||||
uint width;
|
||||
uint height;
|
||||
Pixel *front_buffer;
|
||||
Pixel *back_buffer;
|
||||
bool managed_buffer = false;
|
||||
PanelType panel_type;
|
||||
bool inverted_stb = false;
|
||||
COLOR_ORDER color_order;
|
||||
Pixel background = 0;
|
||||
|
||||
// DMA & PIO
|
||||
uint dma_channel = 0;
|
||||
uint dma_flip_channel = 1;
|
||||
volatile bool do_flip = false;
|
||||
int dma_channel = -1;
|
||||
uint bit = 0;
|
||||
uint row = 0;
|
||||
|
||||
|
@ -110,20 +121,22 @@ class Hub75 {
|
|||
unsigned int pin_led_g = 17;
|
||||
unsigned int pin_led_b = 18;
|
||||
|
||||
Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC, false) {};
|
||||
Hub75(uint width, uint height) : Hub75(width, height, nullptr) {};
|
||||
Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC) {};
|
||||
Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type) : Hub75(width, height, buffer, panel_type, false) {};
|
||||
Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb);
|
||||
Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb, COLOR_ORDER color_order=COLOR_ORDER::RGB);
|
||||
~Hub75();
|
||||
|
||||
void FM6126A_write_register(uint16_t value, uint8_t position);
|
||||
void FM6126A_setup();
|
||||
void set_color(uint x, uint y, Pixel c);
|
||||
void set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_hsv(uint x, uint y, float r, float g, float b);
|
||||
|
||||
void set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b);
|
||||
void display_update();
|
||||
void clear();
|
||||
void start(irq_handler_t handler);
|
||||
void stop(irq_handler_t handler);
|
||||
void flip(bool copybuffer=true);
|
||||
void dma_complete();
|
||||
};
|
||||
void update(PicoGraphics *graphics);
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include(${CMAKE_CURRENT_LIST_DIR}/hub75.cmake)
|
|
@ -0,0 +1,11 @@
|
|||
add_library(hub75_legacy INTERFACE)
|
||||
|
||||
target_sources(hub75_legacy INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/hub75.cpp)
|
||||
|
||||
target_include_directories(hub75_legacy INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(hub75_legacy INTERFACE pico_stdlib hardware_pio hardware_dma)
|
||||
|
||||
pico_generate_pio_header(hub75_legacy ${CMAKE_CURRENT_LIST_DIR}/hub75.pio)
|
|
@ -0,0 +1,316 @@
|
|||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "hub75.hpp"
|
||||
|
||||
|
||||
// Basic function to convert Hue, Saturation and Value to an RGB colour
|
||||
Pixel hsv_to_rgb(float h, float s, float v) {
|
||||
if(h < 0.0f) {
|
||||
h = 1.0f + fmod(h, 1.0f);
|
||||
}
|
||||
|
||||
int i = int(h * 6);
|
||||
float f = h * 6 - i;
|
||||
|
||||
v = v * 255.0f;
|
||||
|
||||
float sv = s * v;
|
||||
float fsv = f * sv;
|
||||
|
||||
auto p = uint8_t(-sv + v);
|
||||
auto q = uint8_t(-fsv + v);
|
||||
auto t = uint8_t(fsv - sv + v);
|
||||
|
||||
uint8_t bv = uint8_t(v);
|
||||
|
||||
switch (i % 6) {
|
||||
default:
|
||||
case 0: return Pixel(bv, t, p);
|
||||
case 1: return Pixel(q, bv, p);
|
||||
case 2: return Pixel(p, bv, t);
|
||||
case 3: return Pixel(p, q, bv);
|
||||
case 4: return Pixel(t, p, bv);
|
||||
case 5: return Pixel(bv, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb)
|
||||
: width(width), height(height), panel_type(panel_type), inverted_stb(inverted_stb)
|
||||
{
|
||||
// Set up allllll the GPIO
|
||||
gpio_init(pin_r0); gpio_set_function(pin_r0, GPIO_FUNC_SIO); gpio_set_dir(pin_r0, true); gpio_put(pin_r0, 0);
|
||||
gpio_init(pin_g0); gpio_set_function(pin_g0, GPIO_FUNC_SIO); gpio_set_dir(pin_g0, true); gpio_put(pin_g0, 0);
|
||||
gpio_init(pin_b0); gpio_set_function(pin_b0, GPIO_FUNC_SIO); gpio_set_dir(pin_b0, true); gpio_put(pin_b0, 0);
|
||||
|
||||
gpio_init(pin_r1); gpio_set_function(pin_r1, GPIO_FUNC_SIO); gpio_set_dir(pin_r1, true); gpio_put(pin_r1, 0);
|
||||
gpio_init(pin_g1); gpio_set_function(pin_g1, GPIO_FUNC_SIO); gpio_set_dir(pin_g1, true); gpio_put(pin_g1, 0);
|
||||
gpio_init(pin_b1); gpio_set_function(pin_b1, GPIO_FUNC_SIO); gpio_set_dir(pin_b1, true); gpio_put(pin_b1, 0);
|
||||
|
||||
gpio_init(pin_row_a); gpio_set_function(pin_row_a, GPIO_FUNC_SIO); gpio_set_dir(pin_row_a, true); gpio_put(pin_row_a, 0);
|
||||
gpio_init(pin_row_b); gpio_set_function(pin_row_b, GPIO_FUNC_SIO); gpio_set_dir(pin_row_b, true); gpio_put(pin_row_b, 0);
|
||||
gpio_init(pin_row_c); gpio_set_function(pin_row_c, GPIO_FUNC_SIO); gpio_set_dir(pin_row_c, true); gpio_put(pin_row_c, 0);
|
||||
gpio_init(pin_row_d); gpio_set_function(pin_row_d, GPIO_FUNC_SIO); gpio_set_dir(pin_row_d, true); gpio_put(pin_row_d, 0);
|
||||
gpio_init(pin_row_e); gpio_set_function(pin_row_e, GPIO_FUNC_SIO); gpio_set_dir(pin_row_e, true); gpio_put(pin_row_e, 0);
|
||||
|
||||
gpio_init(pin_clk); gpio_set_function(pin_clk, GPIO_FUNC_SIO); gpio_set_dir(pin_clk, true); gpio_put(pin_clk, !clk_polarity);
|
||||
gpio_init(pin_stb); gpio_set_function(pin_stb, GPIO_FUNC_SIO); gpio_set_dir(pin_stb, true); gpio_put(pin_clk, !stb_polarity);
|
||||
gpio_init(pin_oe); gpio_set_function(pin_oe, GPIO_FUNC_SIO); gpio_set_dir(pin_oe, true); gpio_put(pin_clk, !oe_polarity);
|
||||
|
||||
if (buffer == nullptr) {
|
||||
front_buffer = new Pixel[width * height];
|
||||
back_buffer = new Pixel[width * height];
|
||||
managed_buffer = true;
|
||||
} else {
|
||||
front_buffer = buffer;
|
||||
back_buffer = buffer + width * height;
|
||||
managed_buffer = false;
|
||||
}
|
||||
|
||||
if (brightness == 0) {
|
||||
if (width >= 64) brightness = 6;
|
||||
if (width >= 96) brightness = 3;
|
||||
if (width >= 128) brightness = 2;
|
||||
if (width >= 160) brightness = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::set_color(uint x, uint y, Pixel c) {
|
||||
int offset = 0;
|
||||
if(x >= width || y >= height) return;
|
||||
if(y >= height / 2) {
|
||||
y -= height / 2;
|
||||
offset = (y * width + x) * 2;
|
||||
offset += 1;
|
||||
} else {
|
||||
offset = (y * width + x) * 2;
|
||||
}
|
||||
front_buffer[offset] = c;
|
||||
}
|
||||
|
||||
void Hub75::set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
|
||||
set_color(x, y, Pixel(r, g, b));
|
||||
}
|
||||
|
||||
void Hub75::set_hsv(uint x, uint y, float h, float s, float v) {
|
||||
set_color(x, y, hsv_to_rgb(h, s, v));
|
||||
}
|
||||
|
||||
void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) {
|
||||
gpio_put(pin_clk, !clk_polarity);
|
||||
gpio_put(pin_stb, !stb_polarity);
|
||||
|
||||
uint8_t threshold = width - position;
|
||||
for(auto i = 0u; i < width; i++) {
|
||||
auto j = i % 16;
|
||||
bool b = value & (1 << j);
|
||||
|
||||
gpio_put(pin_r0, b);
|
||||
gpio_put(pin_g0, b);
|
||||
gpio_put(pin_b0, b);
|
||||
gpio_put(pin_r1, b);
|
||||
gpio_put(pin_g1, b);
|
||||
gpio_put(pin_b1, b);
|
||||
|
||||
// Assert strobe/latch if i > threshold
|
||||
// This somehow indicates to the FM6126A which register we want to write :|
|
||||
gpio_put(pin_stb, i > threshold);
|
||||
gpio_put(pin_clk, clk_polarity);
|
||||
sleep_us(10);
|
||||
gpio_put(pin_clk, !clk_polarity);
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::FM6126A_setup() {
|
||||
// Ridiculous register write nonsense for the FM6126A-based 64x64 matrix
|
||||
FM6126A_write_register(0b1111111111111110, 12);
|
||||
FM6126A_write_register(0b0000001000000000, 13);
|
||||
}
|
||||
|
||||
void Hub75::start(irq_handler_t handler) {
|
||||
if(handler) {
|
||||
dma_channel = 0;
|
||||
|
||||
// Try as I might, I can't seem to coax MicroPython into leaving PIO in a known state upon soft reset
|
||||
// check for claimed PIO and prepare a clean slate.
|
||||
stop(handler);
|
||||
|
||||
if (panel_type == PANEL_FM6126A) {
|
||||
FM6126A_setup();
|
||||
}
|
||||
|
||||
// Claim the PIO so we can clean it upon soft restart
|
||||
pio_sm_claim(pio, sm_data);
|
||||
pio_sm_claim(pio, sm_row);
|
||||
|
||||
data_prog_offs = pio_add_program(pio, &hub75_data_rgb888_program);
|
||||
if (inverted_stb) {
|
||||
row_prog_offs = pio_add_program(pio, &hub75_row_inverted_program);
|
||||
} else {
|
||||
row_prog_offs = pio_add_program(pio, &hub75_row_program);
|
||||
}
|
||||
hub75_data_rgb888_program_init(pio, sm_data, data_prog_offs, DATA_BASE_PIN, pin_clk);
|
||||
hub75_row_program_init(pio, sm_row, row_prog_offs, ROWSEL_BASE_PIN, ROWSEL_N_PINS, pin_stb);
|
||||
|
||||
// Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly
|
||||
pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f);
|
||||
|
||||
dma_channel_claim(dma_channel);
|
||||
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
||||
channel_config_set_bswap(&config, false);
|
||||
channel_config_set_dreq(&config, pio_get_dreq(pio, sm_data, true));
|
||||
dma_channel_configure(dma_channel, &config, &pio->txf[sm_data], NULL, 0, false);
|
||||
|
||||
dma_channel_claim(dma_flip_channel);
|
||||
dma_channel_config flip_config = dma_channel_get_default_config(dma_flip_channel);
|
||||
channel_config_set_transfer_data_size(&flip_config, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&flip_config, true);
|
||||
channel_config_set_write_increment(&flip_config, true);
|
||||
channel_config_set_bswap(&flip_config, false);
|
||||
dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false);
|
||||
|
||||
// Same handler for both DMA channels
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, handler);
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, handler);
|
||||
|
||||
dma_channel_set_irq1_enabled(dma_flip_channel, true);
|
||||
dma_channel_set_irq0_enabled(dma_channel, true);
|
||||
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), true);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
irq_set_enabled(DMA_IRQ_1, true);
|
||||
|
||||
row = 0;
|
||||
bit = 0;
|
||||
|
||||
hub75_data_rgb888_set_shift(pio, sm_data, data_prog_offs, bit);
|
||||
dma_channel_set_trans_count(dma_channel, width * 2, false);
|
||||
dma_channel_set_read_addr(dma_channel, &back_buffer, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::stop(irq_handler_t handler) {
|
||||
do_flip = false;
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
irq_set_enabled(DMA_IRQ_1, false);
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), false);
|
||||
|
||||
if(dma_channel_is_claimed(dma_channel)) {
|
||||
dma_channel_set_irq0_enabled(dma_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_0, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_channel);
|
||||
dma_channel_abort(dma_channel);
|
||||
dma_channel_acknowledge_irq0(dma_channel);
|
||||
dma_channel_unclaim(dma_channel);
|
||||
}
|
||||
|
||||
if(dma_channel_is_claimed(dma_flip_channel)){
|
||||
dma_channel_set_irq1_enabled(dma_flip_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_1, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_flip_channel);
|
||||
dma_channel_abort(dma_flip_channel);
|
||||
dma_channel_acknowledge_irq1(dma_flip_channel);
|
||||
dma_channel_unclaim(dma_flip_channel);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_data)) {
|
||||
pio_sm_set_enabled(pio, sm_data, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_data);
|
||||
pio_sm_unclaim(pio, sm_data);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_row)) {
|
||||
pio_sm_set_enabled(pio, sm_row, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_row);
|
||||
pio_sm_unclaim(pio, sm_row);
|
||||
}
|
||||
|
||||
pio_clear_instruction_memory(pio);
|
||||
|
||||
// Make sure the GPIO is in a known good state
|
||||
// since we don't know what the PIO might have done with it
|
||||
gpio_put_masked(0b111111 << pin_r0, 0);
|
||||
gpio_put_masked(0b11111 << pin_row_a, 0);
|
||||
gpio_put(pin_clk, !clk_polarity);
|
||||
gpio_put(pin_clk, !oe_polarity);
|
||||
}
|
||||
|
||||
Hub75::~Hub75() {
|
||||
if (managed_buffer) {
|
||||
delete[] front_buffer;
|
||||
delete[] back_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::clear() {
|
||||
for(auto x = 0u; x < width; x++) {
|
||||
for(auto y = 0u; y < height; y++) {
|
||||
set_rgb(x, y, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hub75::flip(bool copybuffer) {
|
||||
dma_channel_config flip_config = dma_get_channel_config(dma_flip_channel);
|
||||
channel_config_set_read_increment(&flip_config, copybuffer);
|
||||
dma_channel_configure(dma_flip_channel, &flip_config, nullptr, nullptr, 0, false);
|
||||
|
||||
dma_channel_set_read_addr(dma_flip_channel, copybuffer ? front_buffer : &background, false);
|
||||
dma_channel_set_write_addr(dma_flip_channel, back_buffer, false);
|
||||
|
||||
// Flip and block until the front buffer has been prepared
|
||||
do_flip = true;
|
||||
while(do_flip) {
|
||||
best_effort_wfe_or_timeout(make_timeout_time_us(10));
|
||||
};
|
||||
}
|
||||
|
||||
void Hub75::dma_complete() {
|
||||
if(dma_channel_get_irq1_status(dma_flip_channel)) {
|
||||
dma_channel_acknowledge_irq1(dma_flip_channel);
|
||||
do_flip = false;
|
||||
}
|
||||
|
||||
if(dma_channel_get_irq0_status(dma_channel)) {
|
||||
dma_channel_acknowledge_irq0(dma_channel);
|
||||
|
||||
// Push out a dummy pixel for each row
|
||||
pio_sm_put_blocking(pio, sm_data, 0);
|
||||
pio_sm_put_blocking(pio, sm_data, 0);
|
||||
|
||||
// SM is finished when it stalls on empty TX FIFO
|
||||
hub75_wait_tx_stall(pio, sm_data);
|
||||
|
||||
// Check that previous OEn pulse is finished, else things WILL get out of sequence
|
||||
hub75_wait_tx_stall(pio, sm_row);
|
||||
|
||||
// Latch row data, pulse output enable for new row.
|
||||
pio_sm_put_blocking(pio, sm_row, row | (brightness << 5 << bit));
|
||||
|
||||
if (do_flip && bit == 0 && row == 0) {
|
||||
// Literally flip the front and back buffers by swapping their addresses
|
||||
Pixel *tmp = back_buffer;
|
||||
back_buffer = front_buffer;
|
||||
front_buffer = tmp;
|
||||
// Then, read the contents of the back buffer into the front buffer
|
||||
dma_channel_set_trans_count(dma_flip_channel, width * height, true);
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
if(row == height / 2) {
|
||||
row = 0;
|
||||
bit++;
|
||||
if (bit == BIT_DEPTH) {
|
||||
bit = 0;
|
||||
}
|
||||
hub75_data_rgb888_set_shift(pio, sm_data, data_prog_offs, bit);
|
||||
}
|
||||
|
||||
dma_channel_set_trans_count(dma_channel, width * 2, false);
|
||||
dma_channel_set_read_addr(dma_channel, &back_buffer[row * width * 2], true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "hub75.pio.h"
|
||||
#endif
|
||||
|
||||
const uint DATA_BASE_PIN = 0;
|
||||
const uint DATA_N_PINS = 6;
|
||||
const uint ROWSEL_BASE_PIN = 6;
|
||||
const uint ROWSEL_N_PINS = 5;
|
||||
const uint BIT_DEPTH = 10;
|
||||
|
||||
// This gamma table is used to correct our 8-bit (0-255) colours up to 11-bit,
|
||||
// allowing us to gamma correct without losing dynamic range.
|
||||
constexpr uint16_t GAMMA_10BIT[256] = {
|
||||
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
|
||||
8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
|
||||
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 25,
|
||||
26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47,
|
||||
49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78,
|
||||
80, 82, 85, 87, 89, 92, 94, 96, 99, 101, 104, 106, 109, 112, 114, 117,
|
||||
120, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164,
|
||||
168, 171, 174, 178, 181, 185, 188, 192, 195, 199, 202, 206, 210, 214, 217, 221,
|
||||
225, 229, 233, 237, 241, 245, 249, 253, 257, 261, 265, 270, 274, 278, 283, 287,
|
||||
291, 296, 300, 305, 309, 314, 319, 323, 328, 333, 338, 343, 347, 352, 357, 362,
|
||||
367, 372, 378, 383, 388, 393, 398, 404, 409, 414, 420, 425, 431, 436, 442, 447,
|
||||
453, 459, 464, 470, 476, 482, 488, 494, 499, 505, 511, 518, 524, 530, 536, 542,
|
||||
548, 555, 561, 568, 574, 580, 587, 593, 600, 607, 613, 620, 627, 633, 640, 647,
|
||||
654, 661, 668, 675, 682, 689, 696, 703, 711, 718, 725, 733, 740, 747, 755, 762,
|
||||
770, 777, 785, 793, 800, 808, 816, 824, 832, 839, 847, 855, 863, 872, 880, 888,
|
||||
896, 904, 912, 921, 929, 938, 946, 954, 963, 972, 980, 989, 997, 1006, 1015, 1023
|
||||
};
|
||||
|
||||
|
||||
struct Pixel {
|
||||
uint32_t color;
|
||||
constexpr Pixel() : color(0) {};
|
||||
constexpr Pixel(uint32_t color) : color(color) {};
|
||||
constexpr Pixel(uint8_t r, uint8_t g, uint8_t b) : color((GAMMA_10BIT[b] << 20) | (GAMMA_10BIT[g] << 10) | GAMMA_10BIT[r]) {};
|
||||
};
|
||||
|
||||
enum PanelType {
|
||||
PANEL_GENERIC = 0,
|
||||
PANEL_FM6126A,
|
||||
};
|
||||
|
||||
Pixel hsv_to_rgb(float h, float s, float v);
|
||||
|
||||
class Hub75 {
|
||||
public:
|
||||
uint width;
|
||||
uint height;
|
||||
Pixel *front_buffer;
|
||||
Pixel *back_buffer;
|
||||
bool managed_buffer = false;
|
||||
PanelType panel_type;
|
||||
bool inverted_stb = false;
|
||||
Pixel background = 0;
|
||||
|
||||
// DMA & PIO
|
||||
uint dma_channel = 0;
|
||||
uint dma_flip_channel = 1;
|
||||
volatile bool do_flip = false;
|
||||
uint bit = 0;
|
||||
uint row = 0;
|
||||
|
||||
PIO pio = pio0;
|
||||
uint sm_data = 0;
|
||||
uint sm_row = 1;
|
||||
|
||||
uint data_prog_offs = 0;
|
||||
uint row_prog_offs = 0;
|
||||
|
||||
uint brightness = 6;
|
||||
|
||||
|
||||
// Top half of display - 16 rows on a 32x32 panel
|
||||
unsigned int pin_r0 = 0;
|
||||
unsigned int pin_g0 = 1;
|
||||
unsigned int pin_b0 = 2;
|
||||
|
||||
// Bottom half of display - 16 rows on a 64x64 panel
|
||||
unsigned int pin_r1 = 3;
|
||||
unsigned int pin_g1 = 4;
|
||||
unsigned int pin_b1 = 5;
|
||||
|
||||
// Address pins, 5 lines = 2^5 = 32 values (max 64x64 display)
|
||||
unsigned int pin_row_a = 6;
|
||||
unsigned int pin_row_b = 7;
|
||||
unsigned int pin_row_c = 8;
|
||||
unsigned int pin_row_d = 9;
|
||||
unsigned int pin_row_e = 10;
|
||||
|
||||
// Sundry things
|
||||
unsigned int pin_clk = 11; // Clock
|
||||
unsigned int pin_stb = 12; // Strobe/Latch
|
||||
unsigned int pin_oe = 13; // Output Enable
|
||||
|
||||
const bool clk_polarity = 1;
|
||||
const bool stb_polarity = 1;
|
||||
const bool oe_polarity = 0;
|
||||
|
||||
// User buttons and status LED
|
||||
unsigned int pin_sw_a = 14;
|
||||
unsigned int pin_sw_user = 23;
|
||||
|
||||
unsigned int pin_led_r = 16;
|
||||
unsigned int pin_led_g = 17;
|
||||
unsigned int pin_led_b = 18;
|
||||
|
||||
Hub75(uint width, uint height, Pixel *buffer) : Hub75(width, height, buffer, PANEL_GENERIC, false) {};
|
||||
Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type) : Hub75(width, height, buffer, panel_type, false) {};
|
||||
Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool inverted_stb);
|
||||
~Hub75();
|
||||
|
||||
void FM6126A_write_register(uint16_t value, uint8_t position);
|
||||
void FM6126A_setup();
|
||||
void set_color(uint x, uint y, Pixel c);
|
||||
void set_rgb(uint x, uint y, uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_hsv(uint x, uint y, float r, float g, float b);
|
||||
void display_update();
|
||||
void clear();
|
||||
void start(irq_handler_t handler);
|
||||
void stop(irq_handler_t handler);
|
||||
void flip(bool copybuffer=true);
|
||||
void dma_complete();
|
||||
};
|
|
@ -0,0 +1,158 @@
|
|||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program hub75_row
|
||||
|
||||
; side-set pin 0 is LATCH
|
||||
; side-set pin 1 is OEn
|
||||
; OUT pins are row select A-E
|
||||
;
|
||||
; Each FIFO record consists of:
|
||||
; - 5-bit row select (LSBs)
|
||||
; - Pulse width - 1 (27 MSBs)
|
||||
;
|
||||
; Repeatedly select a row, pulse LATCH, and generate a pulse of a certain
|
||||
; width on OEn.
|
||||
|
||||
.side_set 2
|
||||
|
||||
.wrap_target
|
||||
out pins, 5 [1] side 0x2 ; Deassert OEn, output row select
|
||||
out x, 27 [7] side 0x3 ; Pulse LATCH, get OEn pulse width
|
||||
pulse_loop:
|
||||
jmp x-- pulse_loop side 0x0 ; Assert OEn for x+1 cycles
|
||||
.wrap
|
||||
|
||||
.program hub75_row_inverted
|
||||
|
||||
; side-set pin 0 is LATCH
|
||||
; side-set pin 1 is OEn
|
||||
; OUT pins are row select A-E
|
||||
;
|
||||
; Each FIFO record consists of:
|
||||
; - 5-bit row select (LSBs)
|
||||
; - Pulse width - 1 (27 MSBs)
|
||||
;
|
||||
; Repeatedly select a row, pulse LATCH, and generate a pulse of a certain
|
||||
; width on OEn.
|
||||
|
||||
.side_set 2
|
||||
|
||||
.wrap_target
|
||||
out pins, 5 [1] side 0x3 ; Deassert OEn, output row select
|
||||
out x, 27 [7] side 0x2 ; Pulse LATCH, get OEn pulse width
|
||||
pulse_loop:
|
||||
jmp x-- pulse_loop side 0x1 ; Assert OEn for x+1 cycles
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
static inline void hub75_row_program_init(PIO pio, uint sm, uint offset, uint row_base_pin, uint n_row_pins, uint latch_base_pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, row_base_pin, n_row_pins, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, latch_base_pin, 2, true);
|
||||
for (uint i = row_base_pin; i < row_base_pin + n_row_pins; ++i)
|
||||
pio_gpio_init(pio, i);
|
||||
pio_gpio_init(pio, latch_base_pin);
|
||||
pio_gpio_init(pio, latch_base_pin + 1);
|
||||
|
||||
pio_sm_config c = hub75_row_program_get_default_config(offset);
|
||||
sm_config_set_out_pins(&c, row_base_pin, n_row_pins);
|
||||
sm_config_set_sideset_pins(&c, latch_base_pin);
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
static inline void hub75_wait_tx_stall(PIO pio, uint sm) {
|
||||
uint32_t txstall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + sm);
|
||||
pio->fdebug = txstall_mask;
|
||||
while (!(pio->fdebug & txstall_mask))
|
||||
tight_loop_contents();
|
||||
}
|
||||
%}
|
||||
|
||||
.program hub75_data_rgb888
|
||||
.side_set 1
|
||||
|
||||
; Each FIFO record consists of a RGB888 pixel. (This is ok for e.g. an RGB565
|
||||
; source which has been gamma-corrected)
|
||||
;
|
||||
; Even pixels are sent on R0, G0, B0 and odd pixels on R1, G1, B1 (typically
|
||||
; these are for different parts of the screen, NOT for adjacent pixels, so the
|
||||
; frame buffer must be interleaved before passing to PIO.)
|
||||
;
|
||||
; Each pass through, we take bit n, n + 8 and n + 16 from each pixel, for n in
|
||||
; {0...7}. Therefore the pixels need to be transmitted 8 times (ouch) to build
|
||||
; up the full 8 bit value for each channel, and perform bit-planed PWM by
|
||||
; varying pulse widths on the other state machine, in ascending powers of 2.
|
||||
; This avoids a lot of bit shuffling on the processors, at the cost of DMA
|
||||
; bandwidth (which we have loads of).
|
||||
|
||||
; Might want to close your eyes before you read this
|
||||
public entry_point:
|
||||
.wrap_target
|
||||
|
||||
public shift0: ; R0 G0 B0 (Top half of 64x64 displays)
|
||||
pull side 0 ; gets patched to `out null, n` if n nonzero (otherwise the PULL is required for fencing)
|
||||
in osr, 1 side 0 ; Red0 N
|
||||
out null, 10 side 0 ; Red0 discard
|
||||
|
||||
in osr, 1 side 0 ; Green0 N
|
||||
out null, 10 side 0 ; Green0 discard
|
||||
|
||||
in osr, 1 side 0 ; Blue0 N
|
||||
out null, 32 side 0 ; Remainder discard
|
||||
|
||||
public shift1: ; R1 G1 B1 (Bottom half of 64x64 displays)
|
||||
pull side 0 ; gets patched to `out null, n` if n nonzero (otherwise the PULL is required for fencing)
|
||||
in osr, 1 side 1 ; Red1 N
|
||||
out null, 10 side 1 ; Red1 discard
|
||||
|
||||
in osr, 1 side 1 ; Green1 N
|
||||
out null, 10 side 1 ; Green1 discard
|
||||
|
||||
in osr, 1 side 1 ; Blue1 N
|
||||
out null, 32 side 1 ; Remainder discard
|
||||
|
||||
in null, 26 side 1 ; Note we are just doing this little manoeuvre here to get GPIOs in the order
|
||||
mov pins, ::isr side 1 ; R0, G0, B0, R1, G1, B1. Can go 1 cycle faster if reversed
|
||||
|
||||
.wrap
|
||||
; Note that because the clock edge for pixel n is in the middle of pixel n +
|
||||
; 1, a dummy pixel at the end is required to clock the last piece of genuine
|
||||
; data. (Also 1 pixel of garbage is clocked out at the start, but this is
|
||||
; harmless)
|
||||
|
||||
% c-sdk {
|
||||
static inline void hub75_data_rgb888_program_init(PIO pio, uint sm, uint offset, uint rgb_base_pin, uint clock_pin) {
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, rgb_base_pin, 6, true);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clock_pin, 1, true);
|
||||
for (uint i = rgb_base_pin; i < rgb_base_pin + 6; ++i)
|
||||
pio_gpio_init(pio, i);
|
||||
pio_gpio_init(pio, clock_pin);
|
||||
|
||||
pio_sm_config c = hub75_data_rgb888_program_get_default_config(offset);
|
||||
sm_config_set_out_pins(&c, rgb_base_pin, 6);
|
||||
sm_config_set_sideset_pins(&c, clock_pin);
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
// ISR shift to left. R0 ends up at bit 5. We push it up to MSB and then flip the register.
|
||||
sm_config_set_in_shift(&c, false, false, 32);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_exec(pio, sm, offset + hub75_data_rgb888_offset_entry_point);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
// Patch a data program at `offset` to preshift pixels by `shamt`
|
||||
static inline void hub75_data_rgb888_set_shift(PIO pio, uint sm, uint offset, uint shamt) {
|
||||
uint16_t instr;
|
||||
if (shamt == 0)
|
||||
instr = pio_encode_pull(false, true); // blocking PULL
|
||||
else
|
||||
instr = pio_encode_out(pio_null, shamt);
|
||||
pio->instr_mem[offset + hub75_data_rgb888_offset_shift0] = instr;
|
||||
pio->instr_mem[offset + hub75_data_rgb888_offset_shift1] = instr;
|
||||
}
|
||||
%}
|
|
@ -39,13 +39,13 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void ICP10125::reset() {
|
||||
uint16_t command = __bswap16(SOFT_RESET);
|
||||
uint16_t command = __builtin_bswap16(SOFT_RESET);
|
||||
i2c->write_blocking(address, (uint8_t *)&command, 2, false);
|
||||
sleep_ms(10); // Soft reset time is 170us but you can never be too sure...
|
||||
}
|
||||
|
||||
ICP10125::reading ICP10125::measure(meas_command cmd) {
|
||||
uint16_t command = __bswap16(cmd);
|
||||
uint16_t command = __builtin_bswap16(cmd);
|
||||
reading result = {0.0f, 0.0f, OK};
|
||||
uint16_result results[3];
|
||||
|
||||
|
@ -74,9 +74,9 @@ namespace pimoroni {
|
|||
if(results[1].crc8 != crc8((uint8_t *)&results[1].data, 2)) {result.status = CRC_FAIL; return result;};
|
||||
if(results[2].crc8 != crc8((uint8_t *)&results[2].data, 2)) {result.status = CRC_FAIL; return result;};
|
||||
|
||||
int temperature = __bswap16(results[0].data);
|
||||
int temperature = __builtin_bswap16(results[0].data);
|
||||
// Due to all the byte swapping nonsense I'm not sure if I've discarded the LLSB or LMSB here...
|
||||
int pressure = ((int32_t)__bswap16(results[1].data) << 8) | (__bswap16(results[2].data >> 8)); // LLSB is discarded
|
||||
int pressure = ((int32_t)__builtin_bswap16(results[1].data) << 8) | (__builtin_bswap16(results[2].data >> 8)); // LLSB is discarded
|
||||
|
||||
process_data(pressure, temperature, &result.pressure, &result.temperature);
|
||||
return result;
|
||||
|
@ -84,7 +84,7 @@ namespace pimoroni {
|
|||
|
||||
int ICP10125::chip_id() {
|
||||
uint16_result result;
|
||||
uint16_t command = __bswap16(READ_ID);
|
||||
uint16_t command = __builtin_bswap16(READ_ID);
|
||||
|
||||
i2c->write_blocking(address, (uint8_t *)&command, 2, true);
|
||||
i2c->read_blocking(address, (uint8_t *)&result, 3, false);
|
||||
|
@ -93,12 +93,12 @@ namespace pimoroni {
|
|||
return -1;
|
||||
}
|
||||
|
||||
return __bswap16(result.data) & 0x3f;
|
||||
return __builtin_bswap16(result.data) & 0x3f;
|
||||
}
|
||||
|
||||
bool ICP10125::read_otp() {
|
||||
uint16_result result[4];
|
||||
uint16_t command = __bswap16(READ_OTP);
|
||||
uint16_t command = __builtin_bswap16(READ_OTP);
|
||||
uint8_t move_address_ptr[] = {
|
||||
MOVE_ADDRESS_PTR >> 8, MOVE_ADDRESS_PTR & 0xff,
|
||||
0x00,
|
||||
|
@ -114,7 +114,7 @@ namespace pimoroni {
|
|||
if(result[x].crc8 != crc8((uint8_t *)&result[x].data, 2)) {
|
||||
return false;
|
||||
}
|
||||
sensor_constants[x] = (float)__bswap16(result[x].data);
|
||||
sensor_constants[x] = (float)__builtin_bswap16(result[x].data);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(inky73.cmake)
|
|
@ -0,0 +1,14 @@
|
|||
if(NOT TARGET shiftregister)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../shiftregister/shiftregister.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME inky73)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
target_sources(${DRIVER_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi shiftregister)
|
|
@ -0,0 +1,197 @@
|
|||
#include "inky73.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
enum reg {
|
||||
PSR = 0x00,
|
||||
PWR = 0x01,
|
||||
POF = 0x02,
|
||||
PFS = 0x03,
|
||||
PON = 0x04,
|
||||
BTST1 = 0x05,
|
||||
BTST2 = 0x06,
|
||||
DSLP = 0x07,
|
||||
BTST3 = 0x08,
|
||||
DTM1 = 0x10,
|
||||
DSP = 0x11,
|
||||
DRF = 0x12,
|
||||
IPC = 0x13,
|
||||
PLL = 0x30,
|
||||
TSC = 0x40,
|
||||
TSE = 0x41,
|
||||
TSW = 0x42,
|
||||
TSR = 0x43,
|
||||
CDI = 0x50,
|
||||
LPD = 0x51,
|
||||
TCON = 0x60,
|
||||
TRES = 0x61,
|
||||
DAM = 0x65,
|
||||
REV = 0x70,
|
||||
FLG = 0x71,
|
||||
AMV = 0x80,
|
||||
VV = 0x81,
|
||||
VDCS = 0x82,
|
||||
T_VDCS = 0x84,
|
||||
AGID = 0x86,
|
||||
CMDH = 0xAA,
|
||||
CCSET =0xE0,
|
||||
PWS = 0xE3,
|
||||
TSSET = 0xE6 // E5 or E6
|
||||
};
|
||||
|
||||
bool Inky73::is_busy() {
|
||||
return !(sr.read() & 128);
|
||||
}
|
||||
|
||||
void Inky73::busy_wait(uint timeout_ms) {
|
||||
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||
while(is_busy() && !time_reached(timeout)) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
||||
void Inky73::reset() {
|
||||
gpio_put(RESET, 0); sleep_ms(10);
|
||||
gpio_put(RESET, 1); sleep_ms(10);
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
void Inky73::init() {
|
||||
// configure spi interface and pins
|
||||
spi_init(spi, 20'000'000);
|
||||
|
||||
gpio_set_function(DC, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(DC, GPIO_OUT);
|
||||
|
||||
gpio_set_function(CS, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(CS, GPIO_OUT);
|
||||
gpio_put(CS, 1);
|
||||
|
||||
gpio_set_function(RESET, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(RESET, GPIO_OUT);
|
||||
gpio_put(RESET, 1);
|
||||
|
||||
gpio_set_function(SCK, GPIO_FUNC_SPI);
|
||||
gpio_set_function(MOSI, GPIO_FUNC_SPI);
|
||||
};
|
||||
|
||||
void Inky73::setup() {
|
||||
reset();
|
||||
busy_wait();
|
||||
|
||||
command(CMDH, {0x49, 0x55, 0x20, 0x08, 0x09, 0x18});
|
||||
command(PWR, {0x3F, 0x00, 0x32, 0x2A, 0x0E, 0x2A});
|
||||
if (rotation == ROTATE_0) {
|
||||
command(PSR, {0x53, 0x69});
|
||||
} else {
|
||||
command(PSR, {0x5F, 0x69});
|
||||
}
|
||||
//command(PSR, {0x5F, 0x69});
|
||||
command(PFS, {0x00, 0x54, 0x00, 0x44});
|
||||
command(BTST1, {0x40, 0x1F, 0x1F, 0x2C});
|
||||
command(BTST2, {0x6F, 0x1F, 0x16, 0x25});
|
||||
command(BTST3, {0x6F, 0x1F, 0x1F, 0x22});
|
||||
command(IPC, {0x00, 0x04});
|
||||
command(PLL, {0x02});
|
||||
command(TSE, {0x00});
|
||||
command(CDI, {0x3F});
|
||||
command(TCON, {0x02, 0x00});
|
||||
command(TRES, {0x03, 0x20, 0x01, 0xE0});
|
||||
command(VDCS, {0x1E});
|
||||
command(T_VDCS, {0x00});
|
||||
command(AGID, {0x00});
|
||||
command(PWS, {0x2F});
|
||||
command(CCSET, {0x00});
|
||||
command(TSSET, {0x00});
|
||||
}
|
||||
|
||||
void Inky73::set_blocking(bool blocking) {
|
||||
this->blocking = blocking;
|
||||
}
|
||||
|
||||
void Inky73::power_off() {
|
||||
busy_wait();
|
||||
command(POF); // turn off
|
||||
}
|
||||
|
||||
void Inky73::command(uint8_t reg, size_t len, const uint8_t *data) {
|
||||
gpio_put(CS, 0);
|
||||
|
||||
gpio_put(DC, 0); // command mode
|
||||
spi_write_blocking(spi, ®, 1);
|
||||
|
||||
if(len > 0) {
|
||||
gpio_put(DC, 1); // data mode
|
||||
spi_write_blocking(spi, (const uint8_t*)data, len);
|
||||
}
|
||||
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
void Inky73::data(size_t len, const uint8_t *data) {
|
||||
gpio_put(CS, 0);
|
||||
gpio_put(DC, 1); // data mode
|
||||
spi_write_blocking(spi, (const uint8_t*)data, len);
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
void Inky73::command(uint8_t reg, std::initializer_list<uint8_t> values) {
|
||||
command(reg, values.size(), (uint8_t *)values.begin());
|
||||
}
|
||||
|
||||
void Inky73::update(PicoGraphics *graphics) {
|
||||
if(graphics->pen_type != PicoGraphics::PEN_INKY7) return; // Incompatible buffer
|
||||
|
||||
if(blocking) {
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
setup();
|
||||
|
||||
gpio_put(CS, 0);
|
||||
|
||||
uint8_t reg = DTM1;
|
||||
gpio_put(DC, 0); // command mode
|
||||
spi_write_blocking(spi, ®, 1);
|
||||
|
||||
gpio_put(DC, 1); // data mode
|
||||
|
||||
uint totalLength = 0;
|
||||
gpio_put(CS, 1);
|
||||
graphics->frame_convert(PicoGraphics::PEN_INKY7, [this, &totalLength](void *buf, size_t length) {
|
||||
if (length > 0) {
|
||||
gpio_put(CS, 0);
|
||||
spi_write_blocking(spi, (const uint8_t*)buf, length);
|
||||
totalLength += length;
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
});
|
||||
|
||||
gpio_put(DC, 0); // data mode
|
||||
|
||||
gpio_put(CS, 1);
|
||||
|
||||
busy_wait();
|
||||
|
||||
command(PON, {0}); // turn on
|
||||
busy_wait();
|
||||
|
||||
command(DRF, {0}); // start display refresh
|
||||
busy_wait();
|
||||
|
||||
if(blocking) {
|
||||
busy_wait();
|
||||
|
||||
command(POF); // turn off
|
||||
}
|
||||
}
|
||||
|
||||
bool Inky73::is_pressed(Button button) {
|
||||
return sr.read() & button;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "common/pimoroni_bus.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "drivers/shiftregister/shiftregister.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
class Inky73 : public DisplayDriver {
|
||||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
|
||||
|
||||
// interface pins with our standard defaults where appropriate
|
||||
uint CS = SPI_BG_FRONT_CS;
|
||||
uint DC = 28; // 27;
|
||||
uint SCK = SPI_DEFAULT_SCK;
|
||||
uint MOSI = SPI_DEFAULT_MOSI;
|
||||
uint RESET = 27; //25;
|
||||
|
||||
uint SR_CLOCK = 8;
|
||||
uint SR_LATCH = 9;
|
||||
uint SR_DATA = 10;
|
||||
|
||||
bool blocking = false;
|
||||
|
||||
ShiftRegister<uint8_t> sr = ShiftRegister<uint8_t>(SR_CLOCK, SR_LATCH, SR_DATA);
|
||||
|
||||
public:
|
||||
enum Button : uint8_t {
|
||||
BUTTON_A = 1,
|
||||
BUTTON_B = 2,
|
||||
BUTTON_C = 4,
|
||||
BUTTON_D = 8,
|
||||
BUTTON_E = 16
|
||||
};
|
||||
|
||||
enum colour : uint8_t {
|
||||
BLACK = 0,
|
||||
WHITE = 1,
|
||||
GREEN = 2,
|
||||
BLUE = 3,
|
||||
RED = 4,
|
||||
YELLOW = 5,
|
||||
ORANGE = 6,
|
||||
CLEAN = 7
|
||||
};
|
||||
|
||||
Inky73(uint16_t width, uint16_t height) : Inky73(width, height, ROTATE_0, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
|
||||
|
||||
Inky73(uint16_t width, uint16_t height, SPIPins pins, uint reset=27) : Inky73(width, height, ROTATE_0, pins, reset) {};
|
||||
|
||||
Inky73(uint16_t width, uint16_t height, Rotation rotation, SPIPins pins, uint reset=27) :
|
||||
DisplayDriver(width, height, rotation),
|
||||
spi(pins.spi),
|
||||
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), RESET(reset) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
void busy_wait(uint timeout_ms=45000);
|
||||
void reset();
|
||||
void power_off();
|
||||
|
||||
bool is_busy() override;
|
||||
void update(PicoGraphics *graphics) override;
|
||||
|
||||
void set_blocking(bool blocking);
|
||||
|
||||
bool is_pressed(Button button);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void setup();
|
||||
void command(uint8_t reg, size_t len, const uint8_t *data);
|
||||
void command(uint8_t reg, std::initializer_list<uint8_t> values);
|
||||
void command(uint8_t reg, const uint8_t data) {command(reg, 0, &data);};
|
||||
void command(uint8_t reg) {command(reg, 0, nullptr);};
|
||||
void data(size_t len, const uint8_t *data);
|
||||
};
|
||||
|
||||
}
|
|
@ -320,8 +320,21 @@ namespace pimoroni {
|
|||
Pin::adc(1, 7, 0)}
|
||||
{}
|
||||
|
||||
bool IOExpander::init(bool skipChipIdCheck) {
|
||||
bool succeeded = true;
|
||||
bool IOExpander::init(bool skipChipIdCheck, bool perform_reset) {
|
||||
if(!skipChipIdCheck) {
|
||||
uint16_t chip_id = get_chip_id();
|
||||
if(chip_id != CHIP_ID) {
|
||||
if(debug) {
|
||||
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the chip if requested, to put it into a known state
|
||||
if(perform_reset && !reset()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(interrupt != PIN_UNUSED) {
|
||||
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||
|
@ -331,17 +344,36 @@ namespace pimoroni {
|
|||
enable_interrupt_out(true);
|
||||
}
|
||||
|
||||
if(!skipChipIdCheck) {
|
||||
uint16_t chip_id = get_chip_id();
|
||||
if(chip_id != CHIP_ID) {
|
||||
if(debug) {
|
||||
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
|
||||
}
|
||||
succeeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t IOExpander::check_reset() {
|
||||
uint8_t user_flash_reg = reg::USER_FLASH;
|
||||
uint8_t value;
|
||||
if(i2c_write_blocking(i2c->get_i2c(), address, &user_flash_reg, 1, false) == PICO_ERROR_GENERIC) {
|
||||
return 0x00;
|
||||
}
|
||||
if(i2c_read_blocking(i2c->get_i2c(), address, (uint8_t *)&value, sizeof(uint8_t), false) == PICO_ERROR_GENERIC) {
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IOExpander::reset() {
|
||||
uint32_t start_time = millis();
|
||||
set_bits(reg::CTRL, ctrl_mask::RESET);
|
||||
// Wait for a register to read its initialised value
|
||||
while(check_reset() != 0x78) {
|
||||
sleep_ms(1);
|
||||
if(millis() - start_time >= RESET_TIMEOUT_MS) {
|
||||
if(debug)
|
||||
printf("Timed out waiting for Reset!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
return true;
|
||||
}
|
||||
|
||||
i2c_inst_t* IOExpander::get_i2c() const {
|
||||
|
@ -370,7 +402,7 @@ namespace pimoroni {
|
|||
|
||||
void IOExpander::set_address(uint8_t address) {
|
||||
set_bit(reg::CTRL, 4);
|
||||
i2c->reg_write_uint8(address, reg::ADDR, address);
|
||||
i2c->reg_write_uint8(this->address, reg::ADDR, address);
|
||||
this->address = address;
|
||||
sleep_ms(250); //TODO Handle addr change IOError better
|
||||
//wait_for_flash()
|
||||
|
@ -491,13 +523,35 @@ namespace pimoroni {
|
|||
return divider_good;
|
||||
}
|
||||
|
||||
void IOExpander::set_pwm_period(uint16_t value, bool load) {
|
||||
void IOExpander::set_pwm_period(uint16_t value, bool load, bool wait_for_load) {
|
||||
value &= 0xffff;
|
||||
i2c->reg_write_uint8(address, reg::PWMPL, (uint8_t)(value & 0xff));
|
||||
i2c->reg_write_uint8(address, reg::PWMPH, (uint8_t)(value >> 8));
|
||||
|
||||
if(load)
|
||||
pwm_load();
|
||||
pwm_load(wait_for_load);
|
||||
}
|
||||
|
||||
uint16_t IOExpander::set_pwm_frequency(float frequency, bool load, bool wait_for_load) {
|
||||
uint32_t period = (uint32_t)(CLOCK_FREQ / frequency);
|
||||
if (period / 128 > MAX_PERIOD) {
|
||||
return MAX_PERIOD;
|
||||
}
|
||||
if (period < 2) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint8_t divider = 1;
|
||||
while ((period > MAX_PERIOD) && (divider < MAX_DIVIDER)) {
|
||||
period >>= 1;
|
||||
divider <<= 1;
|
||||
}
|
||||
|
||||
period = MIN(period, MAX_PERIOD); // Should be unnecessary because of earlier raised errors, but kept in case
|
||||
set_pwm_control(divider);
|
||||
set_pwm_period((uint16_t)(period - 1), load, wait_for_load);
|
||||
|
||||
return (uint16_t)period;
|
||||
}
|
||||
|
||||
uint8_t IOExpander::get_mode(uint8_t pin) {
|
||||
|
@ -669,7 +723,7 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void IOExpander::output(uint8_t pin, uint16_t value, bool load) {
|
||||
void IOExpander::output(uint8_t pin, uint16_t value, bool load, bool wait_for_load) {
|
||||
if(pin < 1 || pin > NUM_PINS) {
|
||||
printf("Pin should be in range 1-14.");
|
||||
return;
|
||||
|
@ -685,7 +739,7 @@ namespace pimoroni {
|
|||
i2c->reg_write_uint8(address, io_pin.reg_pwml, (uint8_t)(value & 0xff));
|
||||
i2c->reg_write_uint8(address, io_pin.reg_pwmh, (uint8_t)(value >> 8));
|
||||
if(load)
|
||||
pwm_load();
|
||||
pwm_load(wait_for_load);
|
||||
}
|
||||
else {
|
||||
if(value == LOW) {
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace pimoroni {
|
|||
static const uint8_t PIN_MODE_PWM = 0b00101; // PWM, Output, Push-Pull mode
|
||||
static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance)
|
||||
|
||||
static const uint32_t RESET_TIMEOUT_MS = 1000;
|
||||
|
||||
public:
|
||||
static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;
|
||||
|
||||
|
@ -45,6 +47,10 @@ namespace pimoroni {
|
|||
static const uint16_t LOW = 0;
|
||||
static const uint16_t HIGH = 1;
|
||||
|
||||
static const uint32_t CLOCK_FREQ = 24000000;
|
||||
static const uint32_t MAX_PERIOD = (1 << 16) - 1;
|
||||
static const uint32_t MAX_DIVIDER = (1 << 7);
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Subclasses
|
||||
|
@ -171,7 +177,12 @@ namespace pimoroni {
|
|||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
bool init(bool skip_chip_id_check = false);
|
||||
bool init(bool skip_chip_id_check = false, bool perform_reset = false);
|
||||
|
||||
private:
|
||||
uint8_t check_reset();
|
||||
public:
|
||||
bool reset();
|
||||
|
||||
// For print access in micropython
|
||||
i2c_inst_t* get_i2c() const;
|
||||
|
@ -198,7 +209,8 @@ namespace pimoroni {
|
|||
void pwm_clear(bool wait_for_clear = true);
|
||||
bool pwm_clearing();
|
||||
bool set_pwm_control(uint8_t divider);
|
||||
void set_pwm_period(uint16_t value, bool load = true);
|
||||
void set_pwm_period(uint16_t value, bool load = true, bool wait_for_load = true);
|
||||
uint16_t set_pwm_frequency(float frequency, bool load = true, bool wait_for_load = true);
|
||||
|
||||
uint8_t get_mode(uint8_t pin);
|
||||
void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false);
|
||||
|
@ -206,7 +218,7 @@ namespace pimoroni {
|
|||
int16_t input(uint8_t pin, uint32_t adc_timeout = 1);
|
||||
float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1);
|
||||
|
||||
void output(uint8_t pin, uint16_t value, bool load = true);
|
||||
void output(uint8_t pin, uint16_t value, bool load = true, bool wait_for_load = true);
|
||||
|
||||
void setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c = 0, bool count_microsteps = false);
|
||||
int16_t read_rotary_encoder(uint8_t channel);
|
||||
|
|
|
@ -8,10 +8,11 @@ namespace pimoroni {
|
|||
static const uint8_t DOT_CHAR_WIDTH = 5;
|
||||
|
||||
struct DotChar {
|
||||
uint16_t code;
|
||||
uint8_t data[DOT_CHAR_WIDTH];
|
||||
};
|
||||
|
||||
static const std::map<uint16_t, DotChar> dotfont = {
|
||||
static const DotChar dotfont[] = {
|
||||
{32, {0x00, 0x00, 0x00, 0x00, 0x00}}, // (space)
|
||||
{33, {0x00, 0x00, 0x5f, 0x00, 0x00}}, // !
|
||||
{34, {0x00, 0x07, 0x00, 0x07, 0x00}}, // "
|
||||
|
|
|
@ -27,8 +27,8 @@ namespace pimoroni {
|
|||
return i2c->get_scl();
|
||||
}
|
||||
|
||||
void LTP305::set_brightness(uint8_t brightness, bool update) {
|
||||
brightness = std::min((uint8_t)MAX_BRIGHTNESS, brightness);
|
||||
void LTP305::set_brightness(uint8_t brightness_, bool update) {
|
||||
brightness = std::min((uint8_t)MAX_BRIGHTNESS, brightness_);
|
||||
if(update)
|
||||
i2c->reg_write_uint8(address, CMD_BRIGHTNESS, brightness);
|
||||
}
|
||||
|
@ -64,13 +64,18 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void LTP305::set_character(uint8_t x, uint16_t ch) {
|
||||
std::map<uint16_t, DotChar>::const_iterator it = dotfont.find(ch);
|
||||
const uint8_t *data = nullptr;
|
||||
for(const auto& c : dotfont) {
|
||||
if(c.code == ch) {
|
||||
data = &c.data[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(it != dotfont.end()) {
|
||||
DotChar dchar = it->second;
|
||||
if(data) {
|
||||
for(uint8_t cx = 0; cx < DOT_CHAR_WIDTH; cx++) {
|
||||
for(uint8_t cy = 0; cy < HEIGHT; cy++) {
|
||||
uint8_t c = dchar.data[cx] & (0b1 << cy);
|
||||
uint8_t c = data[cx] & (0b1 << cy);
|
||||
set_pixel(x + cx, cy, c);
|
||||
}
|
||||
}
|
||||
|
@ -123,4 +128,4 @@ namespace pimoroni {
|
|||
i2c->reg_write_uint8(address, CMD_BRIGHTNESS, brightness);
|
||||
i2c->reg_write_uint8(address, CMD_UPDATE, 0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,30 +2,6 @@
|
|||
#include <algorithm>
|
||||
|
||||
namespace pimoroni {
|
||||
lookup::lookup(std::initializer_list<uint16_t> values) : lut(values) {
|
||||
}
|
||||
|
||||
uint8_t lookup::index(uint16_t value) {
|
||||
auto it = find(lut.begin(), lut.end(), value);
|
||||
|
||||
if(it == lut.end())
|
||||
return 0;
|
||||
|
||||
return it - lut.begin();
|
||||
}
|
||||
|
||||
uint16_t lookup::value(uint8_t index) {
|
||||
return lut[index];
|
||||
}
|
||||
|
||||
pimoroni::lookup LTR559::lookup_led_current({5, 10, 20, 50, 100});
|
||||
pimoroni::lookup LTR559::lookup_led_duty_cycle({25, 50, 75, 100});
|
||||
pimoroni::lookup LTR559::lookup_led_pulse_freq({30, 40, 50, 60, 70, 80, 90, 100});
|
||||
pimoroni::lookup LTR559::lookup_proximity_meas_rate({10, 50, 70, 100, 200, 500, 1000, 2000});
|
||||
pimoroni::lookup LTR559::lookup_light_integration_time({100, 50, 200, 400, 150, 250, 300, 350});
|
||||
pimoroni::lookup LTR559::lookup_light_repeat_rate({50, 100, 200, 500, 1000, 2000});
|
||||
pimoroni::lookup LTR559::lookup_light_gain({1, 2, 4, 8, 0, 0, 48, 96});
|
||||
|
||||
bool LTR559::init() {
|
||||
if(interrupt != PIN_UNUSED) {
|
||||
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||
|
@ -129,7 +105,7 @@ namespace pimoroni {
|
|||
i2c->read_bytes(address, LTR559_ALS_DATA_CH1, (uint8_t *)&als, 4);
|
||||
data.als0 = als[1];
|
||||
data.als1 = als[0];
|
||||
data.gain = this->lookup_light_gain.value((status >> LTR559_ALS_PS_STATUS_ALS_GAIN_SHIFT) & LTR559_ALS_PS_STATUS_ALS_GAIN_MASK);
|
||||
data.gain = lookup_light_gain[(status >> LTR559_ALS_PS_STATUS_ALS_GAIN_SHIFT) & LTR559_ALS_PS_STATUS_ALS_GAIN_MASK];
|
||||
|
||||
data.ratio = 101.0f;
|
||||
|
||||
|
@ -148,7 +124,7 @@ namespace pimoroni {
|
|||
float lux = ((int32_t)data.als0 * ch0_c[ch_idx]) - ((int32_t)data.als1 * ch1_c[ch_idx]);
|
||||
lux /= (float)this->data.integration_time / 100.0f;
|
||||
lux /= (float)this->data.gain;
|
||||
data.lux = (uint16_t)(lux / 10000.0f);
|
||||
data.lux = lux / 10000.0f;
|
||||
}
|
||||
|
||||
return has_updated;
|
||||
|
@ -163,12 +139,12 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void LTR559::proximity_led(uint8_t current, uint8_t duty_cycle, uint8_t pulse_freq, uint8_t num_pulses) {
|
||||
current = lookup_led_current.index(current);
|
||||
current = lookup<lookup_led_current>(current);
|
||||
|
||||
duty_cycle = lookup_led_duty_cycle.index(duty_cycle);
|
||||
duty_cycle = lookup<lookup_led_duty_cycle>(duty_cycle);
|
||||
duty_cycle <<= LTR559_PS_LED_DUTY_CYCLE_SHIFT;
|
||||
|
||||
pulse_freq = lookup_led_pulse_freq.index(pulse_freq);
|
||||
pulse_freq = lookup<lookup_led_pulse_freq>(pulse_freq);
|
||||
pulse_freq <<= LTR559_PS_LED_PULSE_FREQ_SHIFT;
|
||||
|
||||
uint8_t buf = current | duty_cycle | pulse_freq;
|
||||
|
@ -180,7 +156,7 @@ namespace pimoroni {
|
|||
|
||||
void LTR559::light_control(bool active, uint8_t gain) {
|
||||
uint8_t buf = 0;
|
||||
gain = lookup_light_gain.index(gain);
|
||||
gain = lookup<lookup_light_gain>(gain);
|
||||
buf |= gain << LTR559_ALS_CONTROL_GAIN_SHIFT;
|
||||
|
||||
if(active)
|
||||
|
@ -223,8 +199,8 @@ namespace pimoroni {
|
|||
|
||||
void LTR559::light_measurement_rate(uint16_t integration_time, uint16_t rate) {
|
||||
data.integration_time = integration_time;
|
||||
integration_time = lookup_light_integration_time.index(integration_time);
|
||||
rate = lookup_light_repeat_rate.index(rate);
|
||||
integration_time = lookup<lookup_light_integration_time>(integration_time);
|
||||
rate = lookup<lookup_light_repeat_rate>(rate);
|
||||
uint8_t buf = 0;
|
||||
buf |= rate;
|
||||
buf |= integration_time << LTR559_ALS_MEAS_RATE_INTEGRATION_TIME_SHIFT;
|
||||
|
@ -232,7 +208,7 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void LTR559::proximity_measurement_rate(uint16_t rate) {
|
||||
uint8_t buf = lookup_proximity_meas_rate.index(rate);
|
||||
uint8_t buf = lookup<lookup_proximity_meas_rate>(rate);
|
||||
i2c->write_bytes(address, LTR559_PS_MEAS_RATE, &buf, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,18 +94,9 @@ namespace pimoroni {
|
|||
uint16_t integration_time;
|
||||
uint16_t gain;
|
||||
float ratio;
|
||||
uint16_t lux;
|
||||
float lux;
|
||||
} ltr559_reading;
|
||||
|
||||
class lookup {
|
||||
private:
|
||||
std::vector<uint16_t> lut;
|
||||
public:
|
||||
lookup(std::initializer_list<uint16_t> values);
|
||||
uint8_t index(uint16_t value);
|
||||
uint16_t value(uint8_t index);
|
||||
};
|
||||
|
||||
class LTR559 {
|
||||
//--------------------------------------------------
|
||||
// Constants
|
||||
|
@ -131,14 +122,13 @@ namespace pimoroni {
|
|||
const uint8_t address = DEFAULT_I2C_ADDRESS;
|
||||
uint interrupt = PIN_UNUSED;
|
||||
|
||||
static pimoroni::lookup lookup_led_current;
|
||||
static pimoroni::lookup lookup_led_duty_cycle;
|
||||
static pimoroni::lookup lookup_led_pulse_freq;
|
||||
static pimoroni::lookup lookup_proximity_meas_rate;
|
||||
static pimoroni::lookup lookup_light_integration_time;
|
||||
static pimoroni::lookup lookup_light_repeat_rate;
|
||||
static pimoroni::lookup lookup_light_gain;
|
||||
|
||||
static constexpr uint16_t lookup_led_current[5] = {5, 10, 20, 50, 100};
|
||||
static constexpr uint16_t lookup_led_duty_cycle[4] = {25, 50, 75, 100};
|
||||
static constexpr uint16_t lookup_led_pulse_freq[8] = {30, 40, 50, 60, 70, 80, 90, 100};
|
||||
static constexpr uint16_t lookup_proximity_meas_rate[8] = {10, 50, 70, 100, 200, 500, 1000, 2000};
|
||||
static constexpr uint16_t lookup_light_integration_time[8] = {100, 50, 200, 400, 150, 250, 300, 350};
|
||||
static constexpr uint16_t lookup_light_repeat_rate[6] = {50, 100, 200, 500, 1000, 2000};
|
||||
static constexpr uint16_t lookup_light_gain[8] = {1, 2, 4, 8, 0, 0, 48, 96};
|
||||
|
||||
//--------------------------------------------------
|
||||
// Constructors/Destructor
|
||||
|
@ -177,6 +167,15 @@ namespace pimoroni {
|
|||
void proximity_measurement_rate(uint16_t rate);
|
||||
void proximity_offset(uint16_t offset);
|
||||
|
||||
template<auto T>
|
||||
const uint16_t lookup(uint16_t value) {
|
||||
size_t length = sizeof(T) / sizeof(uint16_t);
|
||||
for(auto i = 0u; i < length; i++) {
|
||||
if(T[i] == value) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t bit12_to_uint16(uint16_t value);
|
||||
uint16_t uint16_to_bit12(uint16_t value);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(mlx90640.cmake)
|
|
@ -0,0 +1,50 @@
|
|||
#include "src/headers/MLX90640_I2C_Driver.h"
|
||||
#include "mlx90640.hpp"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
|
||||
static pimoroni::I2C *i2c;
|
||||
|
||||
void MLX90640_I2CConfigure(pimoroni::I2C *i2c_instance) {
|
||||
i2c = i2c_instance;
|
||||
}
|
||||
|
||||
void MLX90640_I2CInit()
|
||||
{
|
||||
// i2c->init(); // Called in constructor
|
||||
}
|
||||
|
||||
int MLX90640_I2CGeneralReset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data)
|
||||
{
|
||||
uint8_t cmd[2] = {(char)(startAddress >> 8), (char)(startAddress & 0xFF)};
|
||||
|
||||
// Set 16-bit register pointer
|
||||
i2c->write_blocking(slaveAddr, cmd, sizeof(cmd), true);
|
||||
// Read result
|
||||
i2c->read_blocking(slaveAddr, (uint8_t*)data, nMemAddressRead * sizeof(uint16_t), false);
|
||||
|
||||
for(auto n = 0u; n < nMemAddressRead; n++) {
|
||||
data[n] = __builtin_bswap16(data[n]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MLX90640_I2CFreqSet(int freq)
|
||||
{
|
||||
// We can't assume we own the I2C instance and can wiggle the baudrate ad-hoc
|
||||
}
|
||||
|
||||
int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data)
|
||||
{
|
||||
uint8_t cmd[4] = {(char)(writeAddress >> 8), (char)(writeAddress & 0x00FF), (char)(data >> 8), (char)(data & 0x00FF)};
|
||||
i2c->write_blocking(slaveAddr, cmd, sizeof(cmd), false);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
set(DRIVER_NAME mlx90640)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
target_sources(${DRIVER_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/functions/MLX90640_API.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/MLX90640_RP2040_I2C_Driver.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mlx90640.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src/headers)
|
|
@ -0,0 +1,92 @@
|
|||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <math.h>
|
||||
#include "src/headers/MLX90640_API.h"
|
||||
#include "mlx90640.hpp"
|
||||
|
||||
|
||||
|
||||
namespace pimoroni {
|
||||
MLX90640::MLX90640_Error MLX90640::setup(int fps){
|
||||
MLX90640_I2CConfigure(i2c_instance);
|
||||
//MLX90640_SetDeviceMode(i2c_address, 0);
|
||||
//MLX90640_SetSubPageRepeat(i2c_address, 0);
|
||||
|
||||
switch(fps){
|
||||
case 1:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b001);
|
||||
break;
|
||||
case 2:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b010);
|
||||
break;
|
||||
case 4:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b011);
|
||||
break;
|
||||
case 8:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b100);
|
||||
break;
|
||||
case 16:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b101);
|
||||
if(i2c_instance->get_baudrate() < 1000000) {
|
||||
return INVALID_BAUDRATE;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b110);
|
||||
if(i2c_instance->get_baudrate() < 1000000) {
|
||||
return INVALID_BAUDRATE;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
MLX90640_SetRefreshRate(i2c_address, 0b111);
|
||||
if(i2c_instance->get_baudrate() < 1000000) {
|
||||
return INVALID_BAUDRATE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("Unsupported framerate: %d", fps);
|
||||
#endif
|
||||
return INVALID_FPS;
|
||||
}
|
||||
//MLX90640_SetChessMode(i2c_address);
|
||||
MLX90640_SetInterleavedMode(i2c_address);
|
||||
//MLX90640_SetResolution(i2c_address, 0);
|
||||
MLX90640_DumpEE(i2c_address, eeMLX90640);
|
||||
MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int MLX90640::get_image(void){
|
||||
MLX90640_I2CConfigure(i2c_instance);
|
||||
|
||||
MLX90640_GetFrameData(i2c_address, frame0);
|
||||
sleep_us(1000);
|
||||
MLX90640_GetFrameData(i2c_address, frame1);
|
||||
|
||||
MLX90640_GetImage(frame0, &mlx90640, mlx90640To);
|
||||
MLX90640_GetImage(frame1, &mlx90640, mlx90640To);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MLX90640::get_frame(void){
|
||||
MLX90640_I2CConfigure(i2c_instance);
|
||||
|
||||
MLX90640_GetFrameData(i2c_address, frame0);
|
||||
sleep_us(1000);
|
||||
MLX90640_GetFrameData(i2c_address, frame1);
|
||||
|
||||
int tr0 = MLX90640_GetTa(frame0, &mlx90640) - reflected_temperature;
|
||||
MLX90640_CalculateTo(frame0, &mlx90640, emissivity, tr0, mlx90640To);
|
||||
int tr1 = MLX90640_GetTa(frame1, &mlx90640) - reflected_temperature;
|
||||
MLX90640_CalculateTo(frame1, &mlx90640, emissivity, tr1, mlx90640To);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include <stdint.h>
|
||||
#include "src/headers/MLX90640_API.h"
|
||||
#include "common/pimoroni_i2c.hpp"
|
||||
|
||||
void MLX90640_I2CConfigure(pimoroni::I2C *i2c_instance);
|
||||
|
||||
#define MLX90640_DEFAULT_I2C_ADDRESS 0x33
|
||||
|
||||
namespace pimoroni {
|
||||
class MLX90640 {
|
||||
public:
|
||||
static const int WIDTH = 32;
|
||||
static const int HEIGHT = 24;
|
||||
|
||||
enum MLX90640_Error {
|
||||
OK = 0,
|
||||
INVALID_BAUDRATE = 1,
|
||||
INVALID_FPS = 2,
|
||||
};
|
||||
|
||||
float mlx90640To[WIDTH * HEIGHT] = {0.0f};
|
||||
float emissivity = 1.0f;
|
||||
float reflected_temperature = 8.0f;
|
||||
|
||||
MLX90640(pimoroni::I2C *i2c_instance, uint i2c_address=MLX90640_DEFAULT_I2C_ADDRESS) : i2c_instance(i2c_instance), i2c_address(i2c_address) {};
|
||||
MLX90640_Error setup(int fps);
|
||||
int get_image(void);
|
||||
int get_frame(void);
|
||||
private:
|
||||
pimoroni::I2C *i2c_instance;
|
||||
uint i2c_address = MLX90640_DEFAULT_I2C_ADDRESS;
|
||||
paramsMLX90640 mlx90640;
|
||||
uint16_t eeMLX90640[832] = {0};
|
||||
uint16_t frame0[834] = {0};
|
||||
uint16_t frame1[834] = {0};
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 4dbbc56957b6cb7fce2f7ba2d6351c3f5446f0cb
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME motor)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm_cluster)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm_cluster.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME motor_cluster)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -29,8 +29,6 @@ namespace motor {
|
|||
}
|
||||
|
||||
MotorCluster::~MotorCluster() {
|
||||
delete[] states;
|
||||
delete[] configs;
|
||||
}
|
||||
|
||||
bool MotorCluster::init() {
|
||||
|
@ -713,10 +711,8 @@ namespace motor {
|
|||
float deadzone, DecayMode mode, bool auto_phase) {
|
||||
uint8_t motor_count = pwms.get_chan_pair_count();
|
||||
if(motor_count > 0) {
|
||||
states = new MotorState[motor_count];
|
||||
configs = new motor_config[motor_count];
|
||||
|
||||
for(uint motor = 0; motor < motor_count; motor++) {
|
||||
configs[motor] = motor_config();
|
||||
states[motor] = MotorState(direction, speed_scale, zeropoint, deadzone);
|
||||
configs[motor].phase = (auto_phase) ? (float)motor / (float)motor_count : 0.0f;
|
||||
configs[motor].mode = mode;
|
||||
|
|
|
@ -22,12 +22,15 @@ namespace motor {
|
|||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
static const uint MAX_MOTOR_CHANNELS = 16;
|
||||
|
||||
private:
|
||||
PWMCluster pwms;
|
||||
uint32_t pwm_period;
|
||||
float pwm_frequency;
|
||||
MotorState* states;
|
||||
motor_config* configs;
|
||||
MotorState states[MAX_MOTOR_CHANNELS];
|
||||
motor_config configs[MAX_MOTOR_CHANNELS];
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
|
|
|
@ -1,189 +1,187 @@
|
|||
#include "pcf85063a.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
// binary coded decimal conversion helper functions
|
||||
uint8_t bcd_encode(uint v) {
|
||||
uint v10 = v / 10, v1 = v - (v10 * 10); return v1 | (v10 << 4); }
|
||||
int8_t bcd_decode(uint v) {
|
||||
uint v10 = (v >> 4) & 0x0f, v1 = v & 0x0f; return v1 + (v10 * 10); }
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
void PCF85063A::init() {
|
||||
if(interrupt != PIN_UNUSED) {
|
||||
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(interrupt, GPIO_IN);
|
||||
gpio_set_pulls(interrupt, false, true);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void PCF85063A::reset() {
|
||||
// magic soft reset command
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_1, 0x58);
|
||||
|
||||
// read the oscillator status bit until it is cleared
|
||||
uint8_t status = 0x80;
|
||||
while(status & 0x80) {
|
||||
// attempt to clear oscillator stop flag, then read it back
|
||||
i2c->reg_write_uint8(address, Registers::OSCILLATOR_STATUS, 0x00);
|
||||
status = i2c->reg_read_uint8(address, Registers::OSCILLATOR_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
// i2c helper methods
|
||||
i2c_inst_t* PCF85063A::get_i2c() const {
|
||||
return i2c->get_i2c();
|
||||
}
|
||||
|
||||
int PCF85063A::get_address() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
int PCF85063A::get_sda() const {
|
||||
return i2c->get_sda();
|
||||
}
|
||||
|
||||
int PCF85063A::get_scl() const {
|
||||
return i2c->get_scl();
|
||||
}
|
||||
|
||||
int PCF85063A::get_int() const {
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
datetime_t PCF85063A::get_datetime() {
|
||||
static uint8_t result[7] = {0};
|
||||
|
||||
i2c->read_bytes(address, Registers::SECONDS, result, 7);
|
||||
|
||||
datetime_t dt = {
|
||||
.year = (int16_t)(bcd_decode(result[6]) + 2000), // offset year
|
||||
.month = ( int8_t) bcd_decode(result[5]),
|
||||
.day = ( int8_t) bcd_decode(result[3]),
|
||||
.dotw = ( int8_t) bcd_decode(result[4]),
|
||||
.hour = ( int8_t) bcd_decode(result[2]),
|
||||
.min = ( int8_t) bcd_decode(result[1]),
|
||||
.sec = ( int8_t) bcd_decode(result[0] & 0x7f) // mask out status bit
|
||||
};
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
void PCF85063A::set_datetime(datetime_t *t) {
|
||||
static uint8_t data[7] = {
|
||||
bcd_encode((uint)t->sec),
|
||||
bcd_encode((uint)t->min),
|
||||
bcd_encode((uint)t->hour),
|
||||
bcd_encode((uint)t->day),
|
||||
bcd_encode((uint)t->dotw),
|
||||
bcd_encode((uint)t->month),
|
||||
bcd_encode((uint)t->year - 2000) // offset year
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECONDS, data, 7);
|
||||
}
|
||||
|
||||
void PCF85063A::set_alarm(int second, int minute, int hour, int day) {
|
||||
uint8_t alarm[5] = {
|
||||
uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80),
|
||||
uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80),
|
||||
uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80),
|
||||
uint8_t(day != PARAM_UNUSED ? bcd_encode( day) : 0x80),
|
||||
uint8_t(0x80)
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::set_weekday_alarm(
|
||||
int second, int minute, int hour, DayOfWeek dotw) {
|
||||
|
||||
uint8_t alarm[5] = {
|
||||
uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80),
|
||||
uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80),
|
||||
uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80),
|
||||
uint8_t(0x80),
|
||||
uint8_t(dotw != DayOfWeek::NONE ? bcd_encode( dotw) : 0x80)
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::enable_alarm_interrupt(bool enable) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits = enable ? (bits | 0x80) : (bits & ~0x80);
|
||||
bits |= 0x40; // ensure alarm flag isn't reset
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
bool PCF85063A::read_alarm_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
return bits & 0x40;
|
||||
}
|
||||
|
||||
void PCF85063A::clear_alarm_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits &= ~0x40;
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::unset_alarm() {
|
||||
uint8_t dummy[5] = {0};
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, dummy, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::set_timer(uint8_t ticks, TimerTickPeriod ttp) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
|
||||
uint8_t timer[2] = {
|
||||
ticks,
|
||||
uint8_t((bits & ~0x18) | (ttp << 3) | 0x04) // mask out current ttp and set new + enable
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::TIMER_VALUE, timer, 2);
|
||||
}
|
||||
|
||||
void PCF85063A::enable_timer_interrupt(bool enable, bool flag_only) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
bits = (bits & ~0x03) | (enable ? 0x02 : 0x00) | (flag_only ? 0x01 : 0x00);
|
||||
i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits);
|
||||
}
|
||||
|
||||
bool PCF85063A::read_timer_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
return bits & 0x08;
|
||||
}
|
||||
|
||||
void PCF85063A::clear_timer_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits &= ~0x08;
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::unset_timer() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
bits &= ~0x04;
|
||||
i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits);
|
||||
}
|
||||
|
||||
// set the speed of (or turn off) the clock output
|
||||
void PCF85063A::set_clock_output(ClockOut co) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits = (bits & ~0x07) | uint8_t(co);
|
||||
i2c->reg_write_uint8(
|
||||
address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::set_byte(uint8_t v) {
|
||||
i2c->reg_write_uint8(address, Registers::RAM_BYTE, v);
|
||||
}
|
||||
|
||||
uint8_t PCF85063A::get_byte() {
|
||||
return i2c->reg_read_uint8(address, Registers::RAM_BYTE);
|
||||
}
|
||||
|
||||
}
|
||||
#include "pcf85063a.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
// binary coded decimal conversion helper functions
|
||||
uint8_t bcd_encode(uint v) {
|
||||
uint v10 = v / 10, v1 = v - (v10 * 10); return v1 | (v10 << 4); }
|
||||
int8_t bcd_decode(uint v) {
|
||||
uint v10 = (v >> 4) & 0x0f, v1 = v & 0x0f; return v1 + (v10 * 10); }
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
void PCF85063A::init() {
|
||||
if(interrupt != PIN_UNUSED) {
|
||||
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(interrupt, GPIO_IN);
|
||||
gpio_set_pulls(interrupt, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void PCF85063A::reset() {
|
||||
// magic soft reset command
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_1, 0x58);
|
||||
|
||||
// read the oscillator status bit until it is cleared
|
||||
uint8_t status = 0x80;
|
||||
while(status & 0x80) {
|
||||
// attempt to clear oscillator stop flag, then read it back
|
||||
i2c->reg_write_uint8(address, Registers::OSCILLATOR_STATUS, 0x00);
|
||||
status = i2c->reg_read_uint8(address, Registers::OSCILLATOR_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
// i2c helper methods
|
||||
i2c_inst_t* PCF85063A::get_i2c() const {
|
||||
return i2c->get_i2c();
|
||||
}
|
||||
|
||||
int PCF85063A::get_address() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
int PCF85063A::get_sda() const {
|
||||
return i2c->get_sda();
|
||||
}
|
||||
|
||||
int PCF85063A::get_scl() const {
|
||||
return i2c->get_scl();
|
||||
}
|
||||
|
||||
int PCF85063A::get_int() const {
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
datetime_t PCF85063A::get_datetime() {
|
||||
uint8_t result[7] = {0};
|
||||
|
||||
i2c->read_bytes(address, Registers::SECONDS, result, 7);
|
||||
|
||||
datetime_t dt = {
|
||||
.year = (int16_t)(bcd_decode(result[6]) + 2000), // offset year
|
||||
.month = ( int8_t) bcd_decode(result[5]),
|
||||
.day = ( int8_t) bcd_decode(result[3]),
|
||||
.dotw = ( int8_t) bcd_decode(result[4]),
|
||||
.hour = ( int8_t) bcd_decode(result[2]),
|
||||
.min = ( int8_t) bcd_decode(result[1]),
|
||||
.sec = ( int8_t) bcd_decode(result[0] & 0x7f) // mask out status bit
|
||||
};
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
void PCF85063A::set_datetime(datetime_t *t) {
|
||||
uint8_t data[7] = {
|
||||
bcd_encode((uint)t->sec),
|
||||
bcd_encode((uint)t->min),
|
||||
bcd_encode((uint)t->hour),
|
||||
bcd_encode((uint)t->day),
|
||||
bcd_encode((uint)t->dotw),
|
||||
bcd_encode((uint)t->month),
|
||||
bcd_encode((uint)t->year - 2000) // offset year
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECONDS, data, 7);
|
||||
}
|
||||
|
||||
void PCF85063A::set_alarm(int second, int minute, int hour, int day) {
|
||||
uint8_t alarm[5] = {
|
||||
uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80),
|
||||
uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80),
|
||||
uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80),
|
||||
uint8_t(day != PARAM_UNUSED ? bcd_encode( day) : 0x80),
|
||||
uint8_t(0x80)
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::set_weekday_alarm(
|
||||
int second, int minute, int hour, DayOfWeek dotw) {
|
||||
|
||||
uint8_t alarm[5] = {
|
||||
uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80),
|
||||
uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80),
|
||||
uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80),
|
||||
uint8_t(0x80),
|
||||
uint8_t(dotw != DayOfWeek::NONE ? bcd_encode( dotw) : 0x80)
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::enable_alarm_interrupt(bool enable) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits = enable ? (bits | 0x80) : (bits & ~0x80);
|
||||
bits |= 0x40; // ensure alarm flag isn't reset
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
bool PCF85063A::read_alarm_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
return bits & 0x40;
|
||||
}
|
||||
|
||||
void PCF85063A::clear_alarm_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits &= ~0x40;
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::unset_alarm() {
|
||||
uint8_t dummy[5] = {0};
|
||||
i2c->write_bytes(address, Registers::SECOND_ALARM, dummy, 5);
|
||||
}
|
||||
|
||||
void PCF85063A::set_timer(uint8_t ticks, TimerTickPeriod ttp) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
|
||||
uint8_t timer[2] = {
|
||||
ticks,
|
||||
uint8_t((bits & ~0x18) | (ttp << 3) | 0x04) // mask out current ttp and set new + enable
|
||||
};
|
||||
|
||||
i2c->write_bytes(address, Registers::TIMER_VALUE, timer, 2);
|
||||
}
|
||||
|
||||
void PCF85063A::enable_timer_interrupt(bool enable, bool flag_only) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
bits = (bits & ~0x03) | (enable ? 0x02 : 0x00) | (flag_only ? 0x01 : 0x00);
|
||||
i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits);
|
||||
}
|
||||
|
||||
bool PCF85063A::read_timer_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
return bits & 0x08;
|
||||
}
|
||||
|
||||
void PCF85063A::clear_timer_flag() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits &= ~0x08;
|
||||
i2c->reg_write_uint8(address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::unset_timer() {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE);
|
||||
bits &= ~0x04;
|
||||
i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits);
|
||||
}
|
||||
|
||||
// set the speed of (or turn off) the clock output
|
||||
void PCF85063A::set_clock_output(ClockOut co) {
|
||||
uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2);
|
||||
bits = (bits & ~0x07) | uint8_t(co);
|
||||
i2c->reg_write_uint8(
|
||||
address, Registers::CONTROL_2, bits);
|
||||
}
|
||||
|
||||
void PCF85063A::set_byte(uint8_t v) {
|
||||
i2c->reg_write_uint8(address, Registers::RAM_BYTE, v);
|
||||
}
|
||||
|
||||
uint8_t PCF85063A::get_byte() {
|
||||
return i2c->reg_read_uint8(address, Registers::RAM_BYTE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/apa102
|
|||
#include <math.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "apa102.pio.h"
|
||||
#endif
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pio.h"
|
||||
|
|
|
@ -14,7 +14,9 @@ found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/ws2812
|
|||
#include <math.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "ws2812.pio.h"
|
||||
#endif
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pio.h"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(psram_display.cmake)
|
|
@ -0,0 +1,10 @@
|
|||
set(DRIVER_NAME psram_display)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
target_sources(${DRIVER_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_spi)
|
|
@ -0,0 +1,75 @@
|
|||
#include "psram_display.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
enum reg {
|
||||
WRITE = 0x02,
|
||||
READ = 0x03,
|
||||
RESET_ENABLE = 0x66,
|
||||
RESET = 0x99
|
||||
};
|
||||
|
||||
void PSRamDisplay::init() {
|
||||
uint baud = spi_init(spi, 31'250'000);
|
||||
printf("PSRam connected at %u\n", baud);
|
||||
gpio_set_function(CS, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(CS, GPIO_OUT);
|
||||
gpio_put(CS, 1);
|
||||
|
||||
gpio_set_function(SCK, GPIO_FUNC_SPI);
|
||||
gpio_set_function(MOSI, GPIO_FUNC_SPI);
|
||||
gpio_set_function(MISO, GPIO_FUNC_SPI);
|
||||
|
||||
gpio_put(CS, 0);
|
||||
uint8_t command_buffer[2] = {RESET_ENABLE, RESET};
|
||||
spi_write_blocking(spi, command_buffer, 2);
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
void PSRamDisplay::write(uint32_t address, size_t len, const uint8_t *data)
|
||||
{
|
||||
gpio_put(CS, 0);
|
||||
uint8_t command_buffer[4] = {WRITE, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
|
||||
spi_write_blocking(spi, command_buffer, 4);
|
||||
spi_write_blocking(spi, data, len);
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
void PSRamDisplay::write(uint32_t address, size_t len, const uint8_t byte)
|
||||
{
|
||||
gpio_put(CS, 0);
|
||||
uint8_t command_buffer[4] = {WRITE, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
|
||||
spi_write_blocking(spi, command_buffer, 4);
|
||||
SpiSetBlocking(byte, len);
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
|
||||
void PSRamDisplay::read(uint32_t address, size_t len, uint8_t *data)
|
||||
{
|
||||
gpio_put(CS, 0);
|
||||
uint8_t command_buffer[4] = {READ, (uint8_t)((address >> 16) & 0xFF), (uint8_t)((address >> 8) & 0xFF), (uint8_t)(address & 0xFF)};
|
||||
spi_write_blocking(spi, command_buffer, 4);
|
||||
spi_read_blocking(spi, 0, data, len);
|
||||
gpio_put(CS, 1);
|
||||
}
|
||||
|
||||
void PSRamDisplay::write_pixel(const Point &p, uint8_t colour)
|
||||
{
|
||||
write(pointToAddress(p), 1, colour);
|
||||
}
|
||||
|
||||
void PSRamDisplay::write_pixel_span(const Point &p, uint l, uint8_t colour)
|
||||
{
|
||||
write(pointToAddress(p), l, colour);
|
||||
}
|
||||
|
||||
void PSRamDisplay::read_pixel_span(const Point &p, uint l, uint8_t *data)
|
||||
{
|
||||
read(pointToAddress(p), l, data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "common/pimoroni_bus.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
|
||||
// 8 MB PSRam
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
class PSRamDisplay : public IDirectDisplayDriver<uint8_t> {
|
||||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
|
||||
|
||||
// interface pins with our standard defaults where appropriate
|
||||
uint CS = 3;
|
||||
uint DC = PIN_UNUSED;
|
||||
uint SCK = SPI_DEFAULT_SCK;
|
||||
uint MOSI = SPI_DEFAULT_MOSI;
|
||||
uint MISO = SPI_DEFAULT_MISO;
|
||||
|
||||
uint32_t start_address = 0;
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
|
||||
absolute_time_t timeout;
|
||||
|
||||
bool blocking = false;
|
||||
|
||||
public:
|
||||
enum colour : uint8_t {
|
||||
BLACK = 0,
|
||||
WHITE = 1,
|
||||
GREEN = 2,
|
||||
BLUE = 3,
|
||||
RED = 4,
|
||||
YELLOW = 5,
|
||||
ORANGE = 6,
|
||||
CLEAN = 7
|
||||
};
|
||||
|
||||
PSRamDisplay(uint16_t width, uint16_t height) : PSRamDisplay(width, height, {PIMORONI_SPI_DEFAULT_INSTANCE, 3, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, PIN_UNUSED, PIN_UNUSED}) {};
|
||||
|
||||
PSRamDisplay(uint16_t width, uint16_t height, SPIPins pins) :
|
||||
spi(pins.spi),
|
||||
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi),
|
||||
width(width), height(height) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
void test(void){
|
||||
|
||||
char writeBuffer[1024];
|
||||
char readBuffer[1024];
|
||||
|
||||
uint mb = 8;
|
||||
|
||||
for(uint k = 0; k < 1024*mb; k++)
|
||||
{
|
||||
sprintf(writeBuffer, "%u", k);
|
||||
|
||||
write(k*1024, strlen(writeBuffer)+1, (uint8_t *)writeBuffer);
|
||||
}
|
||||
|
||||
bool bSame = true;
|
||||
for(uint k = 0; k < 1024*mb && bSame; k++)
|
||||
{
|
||||
sprintf(writeBuffer, "%u", k);
|
||||
read(k*1024, strlen(writeBuffer)+1, (uint8_t *)readBuffer);
|
||||
bSame = strcmp(writeBuffer, readBuffer) ==0;
|
||||
printf("[%u] %s == %s ? %s\n", k, writeBuffer, readBuffer, bSame ? "Success" : "Failure");
|
||||
}
|
||||
}
|
||||
|
||||
void write_pixel(const Point &p, uint8_t colour) override;
|
||||
void write_pixel_span(const Point &p, uint l, uint8_t colour) override;
|
||||
void read_pixel_span(const Point &p, uint l, uint8_t *data) override;
|
||||
|
||||
int __not_in_flash_func(SpiSetBlocking)(const uint16_t uSrc, size_t uLen)
|
||||
{
|
||||
invalid_params_if(SPI, 0 > (int)uLen);
|
||||
// Deliberately overflow FIFO, then clean up afterward, to minimise amount
|
||||
// of APB polling required per halfword
|
||||
for (size_t i = 0; i < uLen; ++i) {
|
||||
while (!spi_is_writable(spi))
|
||||
tight_loop_contents();
|
||||
spi_get_hw(spi)->dr = uSrc;
|
||||
}
|
||||
|
||||
while (spi_is_readable(spi))
|
||||
(void)spi_get_hw(spi)->dr;
|
||||
while (spi_get_hw(spi)->sr & SPI_SSPSR_BSY_BITS)
|
||||
tight_loop_contents();
|
||||
while (spi_is_readable(spi))
|
||||
(void)spi_get_hw(spi)->dr;
|
||||
|
||||
// Don't leave overrun flag set
|
||||
spi_get_hw(spi)->icr = SPI_SSPICR_RORIC_BITS;
|
||||
|
||||
return (int)uLen;
|
||||
}
|
||||
|
||||
private:
|
||||
void init();
|
||||
void write(uint32_t address, size_t len, const uint8_t *data);
|
||||
void write(uint32_t address, size_t len, const uint8_t byte);
|
||||
void read(uint32_t address, size_t len, uint8_t *data);
|
||||
|
||||
uint32_t pointToAddress(const Point &p)
|
||||
{
|
||||
return start_address + (p.y * width) + p.x;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
#include "pwm_cluster.hpp"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "pwm_cluster.pio.h"
|
||||
#endif
|
||||
|
||||
// Uncomment the below line to enable debugging
|
||||
//#define DEBUG_MULTI_PWM
|
||||
|
@ -27,7 +30,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, uint pin_mask, bool loading_zone)
|
|||
, sm(sm)
|
||||
, pin_mask(pin_mask & ((1u << NUM_BANK0_GPIOS) - 1))
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -48,7 +50,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, uint pin_base, uint pin_count, bool loa
|
|||
, sm(sm)
|
||||
, pin_mask(0x00000000)
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -68,7 +69,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, const uint8_t *pins, uint32_t length, b
|
|||
, sm(sm)
|
||||
, pin_mask(0x00000000)
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -90,7 +90,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, std::initializer_list<uint8_t> pins, bo
|
|||
, sm(sm)
|
||||
, pin_mask(0x00000000)
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -111,7 +110,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, const pin_pair *pin_pairs, uint32_t len
|
|||
, sm(sm)
|
||||
, pin_mask(0x00000000)
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -137,7 +135,6 @@ PWMCluster::PWMCluster(PIO pio, uint sm, std::initializer_list<pin_pair> pin_pai
|
|||
, sm(sm)
|
||||
, pin_mask(0x00000000)
|
||||
, channel_count(0)
|
||||
, channels(nullptr)
|
||||
, wrap_level(0)
|
||||
, loading_zone(loading_zone) {
|
||||
|
||||
|
@ -159,8 +156,8 @@ PWMCluster::PWMCluster(PIO pio, uint sm, std::initializer_list<pin_pair> pin_pai
|
|||
|
||||
void PWMCluster::constructor_common() {
|
||||
// Initialise all the channels this PWM will control
|
||||
if(channel_count > 0) {
|
||||
channels = new ChannelState[channel_count];
|
||||
for(uint i = 0; i < channel_count; i++) {
|
||||
channels[i] = ChannelState();
|
||||
}
|
||||
|
||||
// Set up the transition buffers
|
||||
|
@ -216,8 +213,6 @@ PWMCluster::~PWMCluster() {
|
|||
gpio_set_function(channel_to_pin_map[channel], GPIO_FUNC_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] channels;
|
||||
}
|
||||
|
||||
void PWMCluster::dma_interrupt_handler() {
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace pimoroni {
|
|||
public:
|
||||
static const uint BUFFER_SIZE = 64; // Set to 64, the maximum number of single rises and falls for 32 channels within a looping time period
|
||||
static const uint NUM_BUFFERS = 3;
|
||||
static const uint MAX_PWM_CHANNELS = 32;
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
|
@ -104,7 +105,7 @@ namespace pimoroni {
|
|||
int dma_channel;
|
||||
uint pin_mask;
|
||||
uint8_t channel_count;
|
||||
ChannelState* channels;
|
||||
ChannelState channels[NUM_BANK0_GPIOS];
|
||||
uint8_t channel_to_pin_map[NUM_BANK0_GPIOS];
|
||||
uint wrap_level;
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#define _PIO_SPI_H
|
||||
|
||||
#include "hardware/pio.h"
|
||||
#ifndef NO_QSTR
|
||||
#include "spi.pio.h"
|
||||
#endif
|
||||
|
||||
typedef struct pio_spi_inst {
|
||||
PIO pio;
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace servo {
|
|||
}
|
||||
|
||||
Calibration::Calibration()
|
||||
: calibration(nullptr), calibration_size(0), limit_lower(true), limit_upper(true) {
|
||||
: calibration_size(0), limit_lower(true), limit_upper(true) {
|
||||
}
|
||||
|
||||
Calibration::Calibration(CalibrationType default_type)
|
||||
|
@ -19,7 +19,7 @@ namespace servo {
|
|||
}
|
||||
|
||||
Calibration::Calibration(const Calibration &other)
|
||||
: calibration(nullptr), calibration_size(0), limit_lower(other.limit_lower), limit_upper(other.limit_upper) {
|
||||
: calibration_size(0), limit_lower(other.limit_lower), limit_upper(other.limit_upper) {
|
||||
uint size = other.size();
|
||||
apply_blank_pairs(size);
|
||||
for(uint i = 0; i < size; i++) {
|
||||
|
@ -28,10 +28,6 @@ namespace servo {
|
|||
}
|
||||
|
||||
Calibration::~Calibration() {
|
||||
if(calibration != nullptr) {
|
||||
delete[] calibration;
|
||||
calibration = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Calibration &Calibration::operator=(const Calibration &other) {
|
||||
|
@ -57,16 +53,13 @@ namespace servo {
|
|||
}
|
||||
|
||||
void Calibration::apply_blank_pairs(uint size) {
|
||||
if(calibration != nullptr) {
|
||||
delete[] calibration;
|
||||
}
|
||||
|
||||
if(size > 0) {
|
||||
calibration = new Pair[size];
|
||||
for(auto i = 0u; i < size; i++) {
|
||||
calibration[i] = Pair();
|
||||
}
|
||||
calibration_size = size;
|
||||
}
|
||||
else {
|
||||
calibration = nullptr;
|
||||
calibration_size = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace servo {
|
|||
static constexpr float DEFAULT_MID_PULSE = 1500.0f; // in microseconds
|
||||
static constexpr float DEFAULT_MAX_PULSE = 2500.0f; // in microseconds
|
||||
|
||||
static const uint MAX_CALIBRATION_PAIRS = 32;
|
||||
|
||||
private:
|
||||
static constexpr float LOWER_HARD_LIMIT = 400.0f; // The minimum microsecond pulse to send
|
||||
static constexpr float UPPER_HARD_LIMIT = 2600.0f; // The maximum microsecond pulse to send
|
||||
|
@ -110,7 +112,7 @@ namespace servo {
|
|||
// Variables
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
Pair* calibration;
|
||||
Pair calibration[MAX_CALIBRATION_PAIRS];
|
||||
uint calibration_size;
|
||||
bool limit_lower;
|
||||
bool limit_upper;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME servo)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm_cluster)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm_cluster.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME servo_cluster)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -44,8 +44,6 @@ namespace servo {
|
|||
}
|
||||
|
||||
ServoCluster::~ServoCluster() {
|
||||
delete[] states;
|
||||
delete[] servo_phases;
|
||||
}
|
||||
|
||||
bool ServoCluster::init() {
|
||||
|
@ -502,9 +500,6 @@ namespace servo {
|
|||
void ServoCluster::create_servo_states(CalibrationType default_type, bool auto_phase) {
|
||||
uint8_t servo_count = pwms.get_chan_count();
|
||||
if(servo_count > 0) {
|
||||
states = new ServoState[servo_count];
|
||||
servo_phases = new float[servo_count];
|
||||
|
||||
for(uint servo = 0; servo < servo_count; servo++) {
|
||||
states[servo] = ServoState(default_type);
|
||||
servo_phases[servo] = (auto_phase) ? (float)servo / (float)servo_count : 0.0f;
|
||||
|
@ -515,9 +510,6 @@ namespace servo {
|
|||
void ServoCluster::create_servo_states(const Calibration& calibration, bool auto_phase) {
|
||||
uint8_t servo_count = pwms.get_chan_count();
|
||||
if(servo_count > 0) {
|
||||
states = new ServoState[servo_count];
|
||||
servo_phases = new float[servo_count];
|
||||
|
||||
for(uint servo = 0; servo < servo_count; servo++) {
|
||||
states[servo] = ServoState(calibration);
|
||||
servo_phases[servo] = (auto_phase) ? (float)servo / (float)servo_count : 0.0f;
|
||||
|
|
|
@ -12,12 +12,14 @@ namespace servo {
|
|||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
static const uint MAX_SERVO_CHANNELS = 32;
|
||||
private:
|
||||
PWMCluster pwms;
|
||||
uint32_t pwm_period;
|
||||
float pwm_frequency;
|
||||
ServoState* states;
|
||||
float* servo_phases;
|
||||
ServoState states[MAX_SERVO_CHANNELS];
|
||||
float servo_phases[MAX_SERVO_CHANNELS];
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(shiftregister.cmake)
|
|
@ -0,0 +1,10 @@
|
|||
set(DRIVER_NAME shiftregister)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
target_sources(${DRIVER_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib)
|
|
@ -0,0 +1,5 @@
|
|||
#include "shiftregister.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
namespace pimoroni {
|
||||
template<typename T> class ShiftRegister {
|
||||
private:
|
||||
uint CLOCK = 0;
|
||||
uint LATCH = 0;
|
||||
uint DATA = 0;
|
||||
|
||||
public:
|
||||
ShiftRegister(uint clock, uint latch, uint data) :
|
||||
CLOCK(clock),
|
||||
LATCH(latch),
|
||||
DATA(data) {
|
||||
gpio_init(CLOCK);
|
||||
gpio_set_function(CLOCK, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(CLOCK, GPIO_OUT);
|
||||
|
||||
gpio_init(LATCH);
|
||||
gpio_set_function(LATCH, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(LATCH, GPIO_OUT);
|
||||
|
||||
gpio_init(DATA);
|
||||
gpio_set_function(DATA, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(DATA, GPIO_IN);
|
||||
}
|
||||
T read() {
|
||||
gpio_put(LATCH, 0);
|
||||
__asm("NOP;");
|
||||
gpio_put(LATCH, 1);
|
||||
__asm("NOP;");
|
||||
T out = 0;
|
||||
for (auto i = 0u; i < sizeof(T) * 8; i++) {
|
||||
out <<= 1;
|
||||
out |= gpio_get(DATA);
|
||||
gpio_put(CLOCK, 1);
|
||||
__asm("NOP;");
|
||||
gpio_put(CLOCK, 0);
|
||||
__asm("NOP;");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include(st7567.cmake)
|
|
@ -0,0 +1,42 @@
|
|||
# ST7567 Display Driver for Pimoroni LCDs <!-- omit in toc -->
|
||||
|
||||
The ST7567 driver supports Serial (SPI) ST7567 displays and is intended for use with:
|
||||
|
||||
* Pico GFX Pack
|
||||
|
||||
## Setup
|
||||
|
||||
Construct an instance of the ST7567 driver with SPI pins.
|
||||
|
||||
|
||||
SPI:
|
||||
|
||||
```c++
|
||||
ST7567 st7567(WIDTH, HEIGHT, {
|
||||
PIMORONI_SPI_DEFAULT_INSTANCE, // SPI instance
|
||||
SPI_BG_FRONT_CS, // Chip-select
|
||||
SPI_DEFAULT_SCK, // SPI Clock
|
||||
SPI_DEFAULT_MOSI, // SPI Out
|
||||
PIN_UNUSED, // SPI In
|
||||
SPI_DEFAULT_DC, // SPI Data/Command
|
||||
PIN_UNUSED // Backlight
|
||||
});
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Update
|
||||
|
||||
ST7567's `update` accepts an instance of `PicoGraphics` in 1 bit colour mode:
|
||||
|
||||
```c++
|
||||
st7567.update(&graphics);
|
||||
```
|
||||
|
||||
### Set Backlight
|
||||
|
||||
If a backlight pin has been configured, you can set the backlight from 0 to 255:
|
||||
|
||||
```c++
|
||||
st7567.set_backlight(128)
|
||||
```
|
|
@ -0,0 +1,10 @@
|
|||
set(DRIVER_NAME st7567)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
target_sources(${DRIVER_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm pico_graphics)
|
|
@ -0,0 +1,183 @@
|
|||
#include "st7567.hpp"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
enum reg : uint8_t {
|
||||
DISPOFF = 0xAE,//
|
||||
DISPON = 0xAF,//
|
||||
SETSTARTLINE = 0x40,//
|
||||
STARTLINE_MASK = 0x3f,//
|
||||
REG_RATIO = 0x20,//
|
||||
SETPAGESTART = 0xb0,//
|
||||
PAGESTART_MASK = 0x07,//
|
||||
SETCOLL = 0x00, // # 0x00-0x0f: Set lower column address */
|
||||
COLL_MASK = 0x0f,//
|
||||
SETCOLH = 0x10, //# 0x10-0x1f: Set higher column address */
|
||||
COLH_MASK = 0x0F,//
|
||||
SEG_DIR_NORMAL = 0xa0, //# 0xa0: Column address 0 is mapped to SEG0 */
|
||||
SEG_DIR_REV = 0xa1, //# 0xa1: Column address 128 is mapped to S
|
||||
DISPNORMAL = 0xa6, //# 0xa6: Normal display */
|
||||
DISPINVERSE = 0xa7, //# 0xa7: Inverse disp
|
||||
DISPRAM = 0xa4, //# 0xa4: Resume to RAM content display */
|
||||
DISPENTIRE = 0xa5, //# 0xa5: Entire display
|
||||
BIAS_1_9 = 0xa2, //# 0xa2: Select BIAS setting 1/9 */
|
||||
BIAS_1_7 = 0xa3, // # 0xa3: Select BIAS setting
|
||||
ENTER_RMWMODE = 0xe0, //# 0xe0: Enter the Read Modify Write mode */
|
||||
EXIT_RMWMODE = 0xee, //# 0xee: Leave the Read Modify Write mode */
|
||||
EXIT_SOFTRST = 0xe2, // # 0xe2: Software RE
|
||||
SETCOMNORMAL = 0xc0, //# 0xc0: Set COM output direction, normal mode */
|
||||
SETCOMREVERSE = 0xc8, // # 0xc8: Set COM output direction, reverse m
|
||||
POWERCTRL_VF = 0x29, //# 0x29: Control built-in power circuit */
|
||||
POWERCTRL_VR = 0x2a, //# 0x2a: Control built-in power circuit */
|
||||
POWERCTRL_VB = 0x2c, //# 0x2c: Control built-in power circuit */
|
||||
POWERCTRL = 0x2f, // # 0x2c: Control built-in power circ
|
||||
REG_RES_RR0 = 0x21, //# 0x21: Regulation Resistior ratio */
|
||||
REG_RES_RR1 = 0x22, //# 0x22: Regulation Resistior ratio */
|
||||
REG_RES_RR2 = 0x24, // # 0x24: Regulation Resistior ra
|
||||
SETCONTRAST = 0x81, // # 0x81: Set contrast cont
|
||||
SETBOOSTER = 0xf8, //# Set booster level */
|
||||
SETBOOSTER4X = 0x00, //# Set booster level */
|
||||
SETBOOSTER5X = 0x01 , // # Set booster le
|
||||
NOP = 0xe3, //# 0xe3: NOP Command for no operation */
|
||||
STARTBYTES = 0,
|
||||
};
|
||||
|
||||
void ST7567::reset() {
|
||||
if(reset_pin == PIN_UNUSED)
|
||||
return;
|
||||
|
||||
gpio_put(reset_pin, 0); sleep_ms(10);
|
||||
sleep_ms(100);
|
||||
gpio_put(reset_pin, 1); sleep_ms(10);
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
|
||||
void ST7567::init(bool auto_init_sequence) {
|
||||
spi_init(spi, spi_baud);
|
||||
|
||||
gpio_set_function(reset_pin, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(reset_pin, GPIO_OUT);
|
||||
|
||||
gpio_set_function(dc, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(dc, GPIO_OUT);
|
||||
|
||||
gpio_set_function(cs, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(cs, GPIO_OUT);
|
||||
|
||||
gpio_set_function(sck, GPIO_FUNC_SPI);
|
||||
gpio_set_function(mosi, GPIO_FUNC_SPI);
|
||||
|
||||
// if a backlight pin is provided then set it up for
|
||||
// pwm control
|
||||
if(bl != PIN_UNUSED) {
|
||||
pwm_config cfg = pwm_get_default_config();
|
||||
pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535);
|
||||
pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true);
|
||||
gpio_set_function(bl, GPIO_FUNC_PWM);
|
||||
set_backlight(0); // Turn backlight off initially to avoid nasty surprises
|
||||
}
|
||||
|
||||
//reset display
|
||||
reset();
|
||||
|
||||
|
||||
// if auto_init_sequence then send initialisation sequence
|
||||
// for our standard displays based on the width and height
|
||||
if(auto_init_sequence) {
|
||||
command(reg::BIAS_1_7);
|
||||
command(reg::SEG_DIR_NORMAL);
|
||||
command(reg::SETCOMREVERSE);
|
||||
command(reg::DISPNORMAL);
|
||||
command(reg::SETSTARTLINE | 0x00); //Startline from 0x40-0x7F
|
||||
command(reg::POWERCTRL);
|
||||
command(reg::REG_RATIO | 4);
|
||||
command(reg::DISPON);
|
||||
command(reg::SETCONTRAST);
|
||||
command(30); // defalut contrast level
|
||||
}
|
||||
|
||||
if(bl != PIN_UNUSED) {
|
||||
set_backlight(255); // Turn backlight on now surprises have passed
|
||||
}
|
||||
}
|
||||
|
||||
void ST7567::command(uint8_t command, size_t len, const char *data) {
|
||||
gpio_put(cs, 0);
|
||||
|
||||
gpio_put(dc, 0); // command mode
|
||||
spi_write_blocking(spi, &command, 1);
|
||||
gpio_put(cs, 1);
|
||||
|
||||
sleep_us(100);
|
||||
if(data) {
|
||||
gpio_put(cs, 0);
|
||||
gpio_put(dc, 1); // data mode
|
||||
spi_write_blocking(spi, (const uint8_t*)data, len);
|
||||
gpio_put(cs, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Native 16-bit framebuffer update
|
||||
void ST7567::update(PicoGraphics *graphics) {
|
||||
uint8_t *fb = (uint8_t *)graphics->frame_buffer;
|
||||
uint8_t page_buffer[PAGESIZE];
|
||||
uint8_t page_byte_selector;
|
||||
uint8_t page_bit_selector;
|
||||
|
||||
for(uint8_t page=0; page < 8 ; page++) { //select page
|
||||
for(uint16_t pixel_index=0 ; pixel_index < (PAGESIZE * 8) ; pixel_index++) { //cycle through a page worth of bits from the fb
|
||||
page_byte_selector = ((pixel_index % 128));
|
||||
page_bit_selector = (pixel_index / 128);
|
||||
|
||||
if(*fb & (0b10000000 >> (pixel_index % 8))) { // check selected pixel is present
|
||||
page_buffer[page_byte_selector] |= (1 << page_bit_selector);
|
||||
}
|
||||
else {
|
||||
page_buffer[page_byte_selector] &= ~( 1 << page_bit_selector);
|
||||
}
|
||||
|
||||
if((pixel_index % 8) >= 7) { //increment fb pointer at end of byte
|
||||
fb++;
|
||||
}
|
||||
}
|
||||
|
||||
if(graphics->pen_type == PicoGraphics::PEN_1BIT) {
|
||||
command(reg::ENTER_RMWMODE);
|
||||
command(reg::SETPAGESTART | page);
|
||||
command(reg::SETCOLL);
|
||||
command(reg::SETCOLH);
|
||||
gpio_put(dc, 1); // data mode
|
||||
gpio_put(cs, 0);
|
||||
spi_write_blocking(spi, &page_buffer[0], PAGESIZE );
|
||||
gpio_put(cs, 1);
|
||||
gpio_put(dc, 0); // Back to command mode
|
||||
}
|
||||
else { //other pen types incompatable
|
||||
return;
|
||||
}
|
||||
}
|
||||
gpio_put(cs, 1);
|
||||
}
|
||||
|
||||
void ST7567::set_backlight(uint8_t brightness) {
|
||||
// gamma correct the provided 0-255 brightness value onto a
|
||||
// 0-65535 range for the pwm counter
|
||||
float gamma = 2.8;
|
||||
uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f);
|
||||
pwm_set_gpio_level(bl, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/pwm.h"
|
||||
#include "common/pimoroni_bus.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
class ST7567 : public DisplayDriver {
|
||||
//--------------------------------------------------
|
||||
// Constants
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
static const uint8_t ROWS = 64;
|
||||
static const uint8_t COLS = 128;
|
||||
static const uint8_t PAGESIZE = 128;
|
||||
|
||||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
spi_inst_t *spi = spi0;
|
||||
|
||||
uint32_t dma_channel;
|
||||
|
||||
// interface pins with our standard defaults where appropriate
|
||||
uint cs;
|
||||
uint dc;
|
||||
uint sck;
|
||||
uint mosi;
|
||||
uint bl;
|
||||
uint reset_pin=21;
|
||||
|
||||
uint32_t spi_baud = 10000000; //10Mhz
|
||||
|
||||
uint8_t offset_cols = 0;
|
||||
uint8_t offset_rows = 0;
|
||||
|
||||
//--------------------------------------------------
|
||||
// Constructors/Destructor
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
|
||||
ST7567(uint16_t width, uint16_t height, SPIPins pins) :
|
||||
DisplayDriver(width, height, ROTATE_0),
|
||||
spi(pins.spi), cs(pins.cs), dc(pins.dc), sck(pins.sck), mosi(pins.mosi), bl(pins.bl) {
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
void update(PicoGraphics *graphics) override;
|
||||
void set_backlight(uint8_t brightness) override;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void init(bool auto_init_sequence = true);
|
||||
void command(uint8_t command, size_t len = 0, const char *data = NULL);
|
||||
};
|
||||
|
||||
}
|
|
@ -172,8 +172,10 @@ namespace pimoroni {
|
|||
gpio_put(dc, 1); // data mode
|
||||
gpio_put(cs, 0);
|
||||
|
||||
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
|
||||
spi_write_blocking(spi, (const uint8_t*)data, length);
|
||||
graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
|
||||
if (length > 0) {
|
||||
spi_write_blocking(spi, (const uint8_t*)data, length);
|
||||
}
|
||||
});
|
||||
|
||||
gpio_put(cs, 1);
|
||||
|
|
|
@ -89,11 +89,19 @@ namespace pimoroni {
|
|||
if(width == 320 && height == 240) {
|
||||
command(reg::GCTRL, 1, "\x35");
|
||||
command(reg::VCOMS, 1, "\x1f");
|
||||
command(0xd6, 1, "\xa1"); // ???
|
||||
command(reg::GMCTRP1, 14, "\xD0\x08\x11\x08\x0C\x15\x39\x33\x50\x36\x13\x14\x29\x2D");
|
||||
command(reg::GMCTRN1, 14, "\xD0\x08\x10\x08\x06\x06\x39\x44\x51\x0B\x16\x14\x2F\x31");
|
||||
}
|
||||
|
||||
if(width == 240 && height == 135) { // Pico Display Pack (1.14" 240x135)
|
||||
command(reg::VRHS, 1, "\x00"); // VRH Voltage setting
|
||||
command(reg::GCTRL, 1, "\x75"); // VGH and VGL voltages
|
||||
command(reg::VCOMS, 1, "\x3D"); // VCOM voltage
|
||||
command(0xd6, 1, "\xa1"); // ???
|
||||
command(reg::GMCTRP1, 14, "\x70\x04\x08\x09\x09\x05\x2A\x33\x41\x07\x13\x13\x29\x2f");
|
||||
command(reg::GMCTRN1, 14, "\x70\x03\x09\x0A\x09\x06\x2B\x34\x41\x07\x12\x14\x28\x2E");
|
||||
}
|
||||
|
||||
command(reg::INVON); // set inversion mode
|
||||
command(reg::SLPOUT); // leave sleep mode
|
||||
command(reg::DISPON); // turn display on
|
||||
|
@ -110,11 +118,11 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void ST7789::cleanup() {
|
||||
if(spi) return; // SPI mode needs no tear down
|
||||
if(dma_channel_is_claimed(parallel_dma)) {
|
||||
dma_channel_abort(parallel_dma);
|
||||
dma_channel_unclaim(parallel_dma);
|
||||
if(dma_channel_is_claimed(st_dma)) {
|
||||
dma_channel_abort(st_dma);
|
||||
dma_channel_unclaim(st_dma);
|
||||
}
|
||||
if(spi) return; // SPI mode needs no further tear down
|
||||
|
||||
if(pio_sm_is_claimed(parallel_pio, parallel_sm)) {
|
||||
pio_sm_set_enabled(parallel_pio, parallel_sm, false);
|
||||
|
@ -125,8 +133,6 @@ namespace pimoroni {
|
|||
|
||||
void ST7789::configure_display(Rotation rotate) {
|
||||
|
||||
bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90;
|
||||
|
||||
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
|
@ -177,20 +183,30 @@ namespace pimoroni {
|
|||
// Pico Display
|
||||
if(width == 240 && height == 135) {
|
||||
caset[0] = 40; // 240 cols
|
||||
caset[1] = 279;
|
||||
raset[0] = 53; // 135 rows
|
||||
raset[1] = 187;
|
||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
caset[1] = 40 + width - 1;
|
||||
raset[0] = 52; // 135 rows
|
||||
raset[1] = 52 + height - 1;
|
||||
if (rotate == ROTATE_0) {
|
||||
raset[0] += 1;
|
||||
raset[1] += 1;
|
||||
}
|
||||
madctl = rotate == ROTATE_180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||
}
|
||||
|
||||
// Pico Display at 90 degree rotation
|
||||
if(width == 135 && height == 240) {
|
||||
caset[0] = 52; // 135 cols
|
||||
caset[1] = 186;
|
||||
caset[1] = 52 + width - 1;
|
||||
raset[0] = 40; // 240 rows
|
||||
raset[1] = 279;
|
||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
raset[1] = 40 + height - 1;
|
||||
madctl = 0;
|
||||
if (rotate == ROTATE_90) {
|
||||
caset[0] += 1;
|
||||
caset[1] += 1;
|
||||
madctl = MADCTL::COL_ORDER | MADCTL::ROW_ORDER;
|
||||
}
|
||||
madctl = rotate == ROTATE_90 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
}
|
||||
|
||||
// Pico Display 2.0
|
||||
|
@ -199,7 +215,7 @@ namespace pimoroni {
|
|||
caset[1] = 319;
|
||||
raset[0] = 0;
|
||||
raset[1] = 239;
|
||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||
}
|
||||
|
||||
|
@ -209,7 +225,7 @@ namespace pimoroni {
|
|||
caset[1] = 239;
|
||||
raset[0] = 0;
|
||||
raset[1] = 319;
|
||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
}
|
||||
|
||||
// Byte swap the 16bit rows/cols values
|
||||
|
@ -223,37 +239,21 @@ namespace pimoroni {
|
|||
command(reg::MADCTL, 1, (char *)&madctl);
|
||||
}
|
||||
|
||||
void ST7789::write_blocking_parallel_dma(const uint8_t *src, size_t len) {
|
||||
while (dma_channel_is_busy(parallel_dma))
|
||||
void ST7789::write_blocking_dma(const uint8_t *src, size_t len) {
|
||||
while (dma_channel_is_busy(st_dma))
|
||||
;
|
||||
dma_channel_set_trans_count(parallel_dma, len, false);
|
||||
dma_channel_set_read_addr(parallel_dma, src, true);
|
||||
dma_channel_set_trans_count(st_dma, len, false);
|
||||
dma_channel_set_read_addr(st_dma, src, true);
|
||||
}
|
||||
|
||||
void ST7789::write_blocking_parallel(const uint8_t *src, size_t len) {
|
||||
const uint8_t *p = src;
|
||||
while(len--) {
|
||||
// Does not byte align correctly
|
||||
//pio_sm_put_blocking(parallel_pio, parallel_sm, *p);
|
||||
while (pio_sm_is_tx_fifo_full(parallel_pio, parallel_sm))
|
||||
;
|
||||
*(volatile uint8_t*)¶llel_pio->txf[parallel_sm] = *p;
|
||||
p++;
|
||||
}
|
||||
write_blocking_dma(src, len);
|
||||
dma_channel_wait_for_finish_blocking(st_dma);
|
||||
|
||||
uint32_t sm_stall_mask = 1u << (parallel_sm + PIO_FDEBUG_TXSTALL_LSB);
|
||||
parallel_pio->fdebug = sm_stall_mask;
|
||||
while (!(parallel_pio->fdebug & sm_stall_mask))
|
||||
;
|
||||
/*uint32_t mask = 0xff << d0;
|
||||
while(len--) {
|
||||
gpio_put(wr_sck, false);
|
||||
uint8_t v = *src++;
|
||||
gpio_put_masked(mask, v << d0);
|
||||
//asm("nop;");
|
||||
gpio_put(wr_sck, true);
|
||||
asm("nop;");
|
||||
}*/
|
||||
// This may cause a race between PIO and the
|
||||
// subsequent chipselect deassert for the last pixel
|
||||
while(!pio_sm_is_tx_fifo_empty(parallel_pio, parallel_sm))
|
||||
;
|
||||
}
|
||||
|
||||
void ST7789::command(uint8_t command, size_t len, const char *data) {
|
||||
|
@ -284,33 +284,23 @@ namespace pimoroni {
|
|||
|
||||
if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native
|
||||
command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
|
||||
} else if(spi) { // SPI Bus
|
||||
} else {
|
||||
gpio_put(dc, 0); // command mode
|
||||
gpio_put(cs, 0);
|
||||
spi_write_blocking(spi, &cmd, 1);
|
||||
if(spi) { // SPI Bus
|
||||
spi_write_blocking(spi, &cmd, 1);
|
||||
} else { // Parallel Bus
|
||||
write_blocking_parallel(&cmd, 1);
|
||||
}
|
||||
|
||||
gpio_put(dc, 1); // data mode
|
||||
|
||||
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
|
||||
spi_write_blocking(spi, (const uint8_t*)data, length);
|
||||
});
|
||||
|
||||
gpio_put(cs, 1);
|
||||
} else { // Parallel Bus
|
||||
gpio_put(dc, 0); // command mode
|
||||
gpio_put(cs, 0);
|
||||
write_blocking_parallel(&cmd, 1);
|
||||
gpio_put(dc, 1); // data mode
|
||||
|
||||
int scanline = 0;
|
||||
|
||||
graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this, scanline](void *data, size_t length) mutable {
|
||||
write_blocking_parallel_dma((const uint8_t*)data, length);
|
||||
|
||||
// Stall on the last scanline since "data" goes out of scope and is lost
|
||||
scanline++;
|
||||
if(scanline == height) {
|
||||
while (dma_channel_is_busy(parallel_dma))
|
||||
;
|
||||
graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) {
|
||||
if (length > 0) {
|
||||
write_blocking_dma((const uint8_t*)data, length);
|
||||
}
|
||||
else {
|
||||
dma_channel_wait_for_finish_blocking(st_dma);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
#include "hardware/gpio.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "common/pimoroni_bus.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "st7789_parallel.pio.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -39,7 +42,7 @@ namespace pimoroni {
|
|||
uint parallel_sm;
|
||||
PIO parallel_pio;
|
||||
uint parallel_offset;
|
||||
uint parallel_dma;
|
||||
uint st_dma;
|
||||
|
||||
|
||||
// The ST7789 requires 16 ns between SPI rising edges.
|
||||
|
@ -82,18 +85,23 @@ namespace pimoroni {
|
|||
sm_config_set_sideset_pins(&c, wr_sck);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
sm_config_set_out_shift(&c, false, true, 8);
|
||||
sm_config_set_clkdiv(&c, 4);
|
||||
|
||||
// Determine clock divider
|
||||
constexpr uint32_t max_pio_clk = 32 * MHZ;
|
||||
const uint32_t sys_clk_hz = clock_get_hz(clk_sys);
|
||||
const uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk;
|
||||
sm_config_set_clkdiv(&c, clk_div);
|
||||
|
||||
pio_sm_init(parallel_pio, parallel_sm, parallel_offset, &c);
|
||||
pio_sm_set_enabled(parallel_pio, parallel_sm, true);
|
||||
|
||||
|
||||
parallel_dma = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(parallel_dma);
|
||||
st_dma = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(st_dma);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
||||
channel_config_set_bswap(&config, false);
|
||||
channel_config_set_dreq(&config, pio_get_dreq(parallel_pio, parallel_sm, true));
|
||||
dma_channel_configure(parallel_dma, &config, ¶llel_pio->txf[parallel_sm], NULL, 0, false);
|
||||
dma_channel_configure(st_dma, &config, ¶llel_pio->txf[parallel_sm], NULL, 0, false);
|
||||
|
||||
gpio_put(rd_sck, 1);
|
||||
|
||||
|
@ -112,6 +120,13 @@ namespace pimoroni {
|
|||
gpio_set_function(wr_sck, GPIO_FUNC_SPI);
|
||||
gpio_set_function(d0, GPIO_FUNC_SPI);
|
||||
|
||||
st_dma = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(st_dma);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
|
||||
channel_config_set_bswap(&config, false);
|
||||
channel_config_set_dreq(&config, spi_get_dreq(spi, true));
|
||||
dma_channel_configure(st_dma, &config, &spi_get_hw(spi)->dr, NULL, 0, false);
|
||||
|
||||
common_init();
|
||||
}
|
||||
|
||||
|
@ -122,7 +137,7 @@ namespace pimoroni {
|
|||
private:
|
||||
void common_init();
|
||||
void configure_display(Rotation rotate);
|
||||
void write_blocking_parallel_dma(const uint8_t *src, size_t len);
|
||||
void write_blocking_dma(const uint8_t *src, size_t len);
|
||||
void write_blocking_parallel(const uint8_t *src, size_t len);
|
||||
void command(uint8_t command, size_t len = 0, const char *data = NULL);
|
||||
};
|
||||
|
|
|
@ -88,16 +88,35 @@ namespace pimoroni {
|
|||
reset();
|
||||
busy_wait();
|
||||
|
||||
command(0x00, {0xE3, 0x08});
|
||||
command(0x01, {0x37, 0x00, 0x23, 0x23});
|
||||
command(0x03, {0x00});
|
||||
command(0x06, {0xC7, 0xC7, 0x1D});
|
||||
command(0x30, {0x3C});
|
||||
command(0x40, {0x00});
|
||||
command(0x50, {0x37});
|
||||
command(0x60, {0x22});
|
||||
command(0x61, {0x02, 0x58, 0x01, 0xC0});
|
||||
command(0xE3, {0xAA});
|
||||
uint8_t dimensions[4] = {
|
||||
uint8_t(width >> 8),
|
||||
uint8_t(width),
|
||||
uint8_t(height >> 8),
|
||||
uint8_t(height)
|
||||
};
|
||||
|
||||
if (width == 600) {
|
||||
if (rotation == ROTATE_0) {
|
||||
command(PSR, {0xE3, 0x08});
|
||||
} else {
|
||||
command(PSR, {0xEF, 0x08});
|
||||
}
|
||||
} else {
|
||||
if (rotation == ROTATE_0) {
|
||||
command(PSR, {0xA3, 0x08});
|
||||
} else {
|
||||
command(PSR, {0xAF, 0x08});
|
||||
}
|
||||
}
|
||||
command(PWR, {0x37, 0x00, 0x23, 0x23});
|
||||
command(PFS, {0x00});
|
||||
command(BTST, {0xC7, 0xC7, 0x1D});
|
||||
command(PLL, {0x3C});
|
||||
command(TSC, {0x00});
|
||||
command(CDI, {0x37});
|
||||
command(TCON, {0x22});
|
||||
command(TRES, 4, dimensions);
|
||||
command(PWS, {0xAA});
|
||||
|
||||
sleep_ms(100);
|
||||
|
||||
|
@ -154,8 +173,19 @@ namespace pimoroni {
|
|||
spi_write_blocking(spi, ®, 1);
|
||||
|
||||
gpio_put(DC, 1); // data mode
|
||||
graphics->scanline_convert(PicoGraphics::PEN_P4, [this](void *buf, size_t length) {
|
||||
spi_write_blocking(spi, (const uint8_t*)buf, length);
|
||||
|
||||
// HACK: Output 48 rows of data since our buffer is 400px tall
|
||||
// but the display has no offset configuration and H/V scan
|
||||
// are reversed.
|
||||
// Any garbage data will do.
|
||||
// 2px per byte, so we need width * 24 bytes
|
||||
if(height == 400 && rotation == ROTATE_0) {
|
||||
spi_write_blocking(spi, (uint8_t *)graphics->frame_buffer, width * 24);
|
||||
}
|
||||
graphics->frame_convert(PicoGraphics::PEN_P4, [this](void *buf, size_t length) {
|
||||
if (length > 0) {
|
||||
spi_write_blocking(spi, (const uint8_t*)buf, length);
|
||||
}
|
||||
});
|
||||
|
||||
gpio_put(CS, 1);
|
||||
|
|
|
@ -42,10 +42,12 @@ namespace pimoroni {
|
|||
CLEAN = 7
|
||||
};
|
||||
|
||||
UC8159(uint16_t width, uint16_t height) : UC8159(width, height, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
|
||||
UC8159(uint16_t width, uint16_t height) : UC8159(width, height, ROTATE_0, {PIMORONI_SPI_DEFAULT_INSTANCE, SPI_BG_FRONT_CS, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, 28, PIN_UNUSED}) {};
|
||||
|
||||
UC8159(uint16_t width, uint16_t height, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) :
|
||||
DisplayDriver(width, height, ROTATE_0),
|
||||
UC8159(uint16_t width, uint16_t height, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) : UC8159(width, height, ROTATE_0, pins, busy, reset) {};
|
||||
|
||||
UC8159(uint16_t width, uint16_t height, Rotation rotation, SPIPins pins, uint busy=PIN_UNUSED, uint reset=27) :
|
||||
DisplayDriver(width, height, rotation),
|
||||
spi(pins.spi),
|
||||
CS(pins.cs), DC(pins.dc), SCK(pins.sck), MOSI(pins.mosi), BUSY(busy), RESET(reset) {
|
||||
init();
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace pimoroni {
|
|||
i2c->read_blocking(address, (uint8_t *)&value, 2, false);
|
||||
|
||||
// TODO do we need to bswap this return value?
|
||||
return __bswap16(value);
|
||||
return __builtin_bswap16(value);
|
||||
}
|
||||
|
||||
// Read a 32-bit register
|
||||
|
@ -217,7 +217,7 @@ namespace pimoroni {
|
|||
i2c->read_blocking(address, (uint8_t *)&value, 4, false);
|
||||
|
||||
// TODO do we need to bswap this return value?
|
||||
return __bswap32(value);
|
||||
return __builtin_bswap32(value);
|
||||
}
|
||||
|
||||
// set distance mode to Short, Medium, or Long
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
add_subdirectory(breakout_dotmatrix)
|
||||
add_subdirectory(breakout_encoder)
|
||||
add_subdirectory(breakout_encoder_wheel)
|
||||
add_subdirectory(breakout_ioexpander)
|
||||
add_subdirectory(breakout_ltr559)
|
||||
add_subdirectory(breakout_colourlcd160x80)
|
||||
|
@ -24,6 +25,7 @@ add_subdirectory(breakout_scd41)
|
|||
add_subdirectory(breakout_vl53l5cx)
|
||||
add_subdirectory(breakout_pms5003)
|
||||
add_subdirectory(breakout_oled_128x128)
|
||||
add_subdirectory(breakout_mlx90640)
|
||||
|
||||
add_subdirectory(pico_display)
|
||||
add_subdirectory(pico_display_2)
|
||||
|
@ -47,6 +49,7 @@ add_subdirectory(inky_pack)
|
|||
add_subdirectory(inky_frame)
|
||||
|
||||
add_subdirectory(automation2040w)
|
||||
add_subdirectory(plasma_stick)
|
||||
add_subdirectory(plasma2040)
|
||||
add_subdirectory(badger2040)
|
||||
add_subdirectory(tufty2040)
|
||||
|
@ -55,3 +58,7 @@ add_subdirectory(servo2040)
|
|||
add_subdirectory(motor2040)
|
||||
add_subdirectory(inventor2040w)
|
||||
add_subdirectory(encoder)
|
||||
add_subdirectory(galactic_unicorn)
|
||||
add_subdirectory(gfx_pack)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
|
|
|
@ -12,8 +12,10 @@ BME280 bme280(&i2c);
|
|||
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
|
|
@ -15,8 +15,10 @@ I2C i2c(BOARD::BREAKOUT_GARDEN);
|
|||
BME68X bme68x(&i2c);
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
|
|
@ -28,8 +28,10 @@ uint16_t durations[profile_length] = { 5, 2, 10, 30, 5, 5, 5, 5, 5, 5 };
|
|||
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ BMP280 bmp280(&i2c);
|
|||
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
|
|
@ -15,13 +15,18 @@ uint8_t values[HALF_WIDTH] = { 0, 0, 0, 0, 0 };
|
|||
uint8_t next_value = 0;
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
display.init();
|
||||
|
||||
while(true) {
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, true);
|
||||
#endif
|
||||
|
||||
absolute_time_t at = get_absolute_time();
|
||||
uint64_t t = to_us_since_boot(at) / 1000000;
|
||||
|
@ -49,7 +54,9 @@ int main() {
|
|||
|
||||
display.show();
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, false);
|
||||
#endif
|
||||
|
||||
sleep_ms(1000 / HEIGHT);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ void eye(uint8_t x, uint8_t y) {
|
|||
}
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
display.init();
|
||||
|
||||
|
@ -50,7 +52,9 @@ int main() {
|
|||
// This gives our crude blink code time to not look like a random flicker
|
||||
display.show();
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, led_toggle);
|
||||
#endif
|
||||
led_toggle = !led_toggle;
|
||||
|
||||
sleep_ms(100);
|
||||
|
|
|
@ -27,8 +27,10 @@ uint8_t offset = 0;
|
|||
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
display.init();
|
||||
|
||||
|
@ -36,7 +38,9 @@ int main() {
|
|||
display.set_image(image, IMAGE_WIDTH, IMAGE_HEIGHT, offset, 0, true, false, ON_LEVEL, IMAGE_PADDING);
|
||||
display.show();
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, led_toggle);
|
||||
#endif
|
||||
led_toggle = !led_toggle;
|
||||
|
||||
sleep_ms(500);
|
||||
|
|
|
@ -11,8 +11,10 @@ BreakoutDotMatrix display(&i2c);
|
|||
bool led_toggle = false;
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
display.init();
|
||||
display.show();
|
||||
|
@ -37,7 +39,9 @@ int main() {
|
|||
display.set_character(5, right);
|
||||
display.show();
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, led_toggle);
|
||||
#endif
|
||||
led_toggle = !led_toggle;
|
||||
|
||||
sleep_ms(1000 / 60);
|
||||
|
|
|
@ -42,8 +42,10 @@ void count_changed(int16_t count) {
|
|||
}
|
||||
|
||||
int main() {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
@ -57,7 +59,9 @@ int main() {
|
|||
enc.clear_interrupt_flag();
|
||||
|
||||
while(true) {
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, toggle);
|
||||
#endif
|
||||
toggle = !toggle;
|
||||
|
||||
if(enc.get_interrupt_flag()) {
|
||||
|
@ -75,7 +79,9 @@ int main() {
|
|||
}
|
||||
else {
|
||||
printf("Encoder not found :'(\n");
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
add_subdirectory(buttons)
|
||||
add_subdirectory(chase_game)
|
||||
add_subdirectory(clock)
|
||||
add_subdirectory(colour_picker)
|
||||
add_subdirectory(encoder)
|
||||
add_subdirectory(gpio_pwm)
|
||||
add_subdirectory(interrupt)
|
||||
add_subdirectory(led_rainbow)
|
||||
add_subdirectory(stop_watch)
|
|
@ -0,0 +1,77 @@
|
|||
# RGB Encoder Wheel Breakout Examples (C++) <!-- omit in toc -->
|
||||
|
||||
- [Function Examples](#function-examples)
|
||||
- [Buttons](#buttons)
|
||||
- [Encoder](#encoder)
|
||||
- [Interrupt](#interrupt)
|
||||
- [LED Examples](#led-examples)
|
||||
- [LED Rainbow](#led-rainbow)
|
||||
- [Clock](#clock)
|
||||
- [Interactive Examples](#interactive-examples)
|
||||
- [Colour Picker](#colour-picker)
|
||||
- [Stop Watch](#stop-watch)
|
||||
- [Chase Game](#chase-game)
|
||||
- [GPIO Examples](#gpio-examples)
|
||||
- [GPIO PWM](#gpio-pwm)
|
||||
|
||||
|
||||
## Function Examples
|
||||
|
||||
### Buttons
|
||||
[buttons/buttons.cpp](buttons/buttons.cpp)
|
||||
|
||||
A demonstration of reading the 5 buttons on Encoder Wheel.
|
||||
|
||||
|
||||
### Encoder
|
||||
[encoder/encoder.cpp](encoder/encoder.cpp)
|
||||
|
||||
A demonstration of reading the rotary dial of the Encoder Wheel breakout.
|
||||
|
||||
|
||||
### Interrupt
|
||||
[interrupt/interrupt.cpp](interrupt/interrupt.cpp)
|
||||
|
||||
How to read the buttons and rotary dial of the Encoder Wheel breakout, only when an interrupt occurs.
|
||||
|
||||
|
||||
## LED Examples
|
||||
|
||||
### LED Rainbow
|
||||
[led_rainbow/led_rainbow.cpp](led_rainbow/led_rainbow.cpp)
|
||||
|
||||
Displays a rotating rainbow pattern on Encoder Wheel's LED ring.
|
||||
|
||||
|
||||
### Clock
|
||||
[clock/clock.cpp](clock/clock.cpp)
|
||||
|
||||
Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.
|
||||
|
||||
|
||||
## Interactive Examples
|
||||
|
||||
### Colour Picker
|
||||
[colour_picker/colour_picker.cpp](colour_picker/colour_picker.cpp)
|
||||
|
||||
Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.
|
||||
|
||||
|
||||
### Stop Watch
|
||||
[stop_watch/stop_watch.cpp](stop_watch/stop_watch.cpp)
|
||||
|
||||
Display a circular stop-watch on the Encoder Wheel's LED ring.
|
||||
|
||||
|
||||
### Chase Game
|
||||
[chase_game/chase_game.cpp](chase_game/chase_game.cpp)
|
||||
|
||||
A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band to the white goal. The closer to the goal, the greener your coloured band will be. When you reach the goal, the goal will move to a new random position.
|
||||
|
||||
|
||||
## GPIO Examples
|
||||
|
||||
### GPIO PWM
|
||||
[gpio_pwm/gpio_pwm.cpp](gpio_pwm/gpio_pwm.cpp)
|
||||
|
||||
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_buttons)
|
||||
add_executable(${OUTPUT_NAME} buttons.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,96 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
A demonstration of reading the 5 buttons on Encoder Wheel.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
const std::string BUTTON_NAMES[] = {"Up", "Down", "Left", "Right", "Centre"};
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
bool last_pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
bool pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
// Read all of the encoder wheel's buttons
|
||||
for(int b = 0 ; b < NUM_BUTTONS; b++) {
|
||||
pressed[b] = wheel.pressed(b);
|
||||
if(pressed[b] != last_pressed[b]) {
|
||||
printf("%s %s\n", BUTTON_NAMES[b].c_str(), pressed[b] ? "Pressed" : "Released");
|
||||
}
|
||||
last_pressed[b] = pressed[b];
|
||||
}
|
||||
|
||||
// Clear the LED ring
|
||||
wheel.clear();
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
if(i % 6 == 3) {
|
||||
wheel.set_rgb(i, 64, 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
// If up is pressed, set the top LEDs to yellow
|
||||
if(pressed[UP]) {
|
||||
int mid = NUM_LEDS;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If right is pressed, set the right LEDs to red
|
||||
if(pressed[RIGHT]) {
|
||||
int mid = NUM_LEDS / 4;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 255, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If down is pressed, set the bottom LEDs to green
|
||||
if(pressed[DOWN]) {
|
||||
int mid = NUM_LEDS / 2;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 0, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If left is pressed, set the left LEDs to blue
|
||||
if(pressed[LEFT]) {
|
||||
int mid = (NUM_LEDS * 3) / 4;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
// If centre is pressed, set the diagonal LEDs to half white
|
||||
if(pressed[CENTRE]) {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
if(i % 6 >= 2 && i % 6 <= 4) {
|
||||
wheel.set_rgb(i, 128, 128, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_chase_game)
|
||||
add_executable(${OUTPUT_NAME} chase_game.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,148 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band
|
||||
to the white goal. The closer to the goal, the greener your coloured band will be.
|
||||
When you reach the goal, the goal will move to a new random position.
|
||||
*/
|
||||
|
||||
// The band colour hues to show in Angle mode
|
||||
constexpr float GOAL_HUE = 0.333f;
|
||||
constexpr float FAR_HUE = 0.0f;
|
||||
|
||||
// The width and colour settings for the band
|
||||
constexpr float BAND_WIDTH = 5.0f;
|
||||
constexpr float BAND_SATURATION = 1.0f;
|
||||
constexpr float BAND_IN_GOAL_SATURATION = 0.5f;
|
||||
constexpr float BAND_BRIGHTNESS = 1.0f;
|
||||
|
||||
// The width and colour settings for the goal
|
||||
// Goal should be wider than the band by a small amount
|
||||
constexpr float GOAL_MARGIN = 1.0f;
|
||||
constexpr float GOAL_WIDTH = BAND_WIDTH + (2.0f * GOAL_MARGIN);
|
||||
constexpr float GOAL_BRIGHTNESS = 0.4f;
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
float goal_position = 0.0f;
|
||||
int16_t band_position = 0;
|
||||
|
||||
|
||||
// Maps a value from one range to another
|
||||
float map(float x, float in_min, float in_max, float out_min, float out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
// Shows a band and goal with the given widths at the positions on the strip
|
||||
void colour_band(float centre_position, float width, float goal_position, float goal_width, float hue) {
|
||||
if(centre_position >= 0.0f && width > 0.0f && goal_width > 0.0) {
|
||||
float band_start = centre_position - (width / 2);
|
||||
float band_end = centre_position + (width / 2);
|
||||
float band_centre = centre_position;
|
||||
|
||||
float goal_start = goal_position - (goal_width / 2);
|
||||
float goal_end = goal_position + (goal_width / 2);
|
||||
|
||||
// Go through each led in the strip
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
// Set saturation and brightness values for if the led is inside or outside of the goal
|
||||
float saturation = BAND_SATURATION;
|
||||
float brightness = 0.0f;
|
||||
|
||||
if(i >= goal_start && i < goal_end) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
if(goal_end >= NUM_LEDS && i + NUM_LEDS < goal_end) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
if(goal_start < 0 && i - NUM_LEDS >= goal_start) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
|
||||
float val = brightness;
|
||||
float sat = 0.0f;
|
||||
if(i >= band_start && i < band_end) {
|
||||
// Inside the band
|
||||
if(i < band_centre) {
|
||||
// Transition into the band
|
||||
val = map(i, band_centre, band_start, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i, band_centre, band_start, BAND_SATURATION, saturation);
|
||||
}
|
||||
else {
|
||||
val = map(i, band_centre, band_end, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i, band_centre, band_end, BAND_SATURATION, saturation);
|
||||
}
|
||||
}
|
||||
else if(band_end >= NUM_LEDS && i + NUM_LEDS < band_end && i < band_centre) {
|
||||
val = map(i + NUM_LEDS, band_centre, band_end, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i + NUM_LEDS, band_centre, band_end, BAND_SATURATION, saturation);
|
||||
}
|
||||
else if(band_start < 0 && i - NUM_LEDS >= band_start && i >= band_centre) {
|
||||
val = map(i - NUM_LEDS, band_centre, band_start, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i - NUM_LEDS, band_centre, band_start, BAND_SATURATION, saturation);
|
||||
}
|
||||
//else {
|
||||
// Outside of the band
|
||||
//}
|
||||
wheel.set_hsv(i, hue, sat, val);
|
||||
}
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
band_position = wheel.step();
|
||||
|
||||
// Convert the difference between the band and goal positions into a colour hue
|
||||
float diff1, diff2;
|
||||
if(band_position > goal_position) {
|
||||
diff1 = band_position - goal_position;
|
||||
diff2 = (goal_position + NUM_LEDS) - band_position;
|
||||
}
|
||||
else {
|
||||
diff1 = goal_position - band_position;
|
||||
diff2 = (band_position + NUM_LEDS) - goal_position;
|
||||
}
|
||||
|
||||
float position_diff = MIN(diff1, diff2);
|
||||
float hue = map(position_diff, 0, NUM_LEDS / 2.0f, GOAL_HUE, FAR_HUE);
|
||||
|
||||
// Convert the band and goal positions to positions on the LED strip
|
||||
float strip_band_position = map(band_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
|
||||
float strip_goal_position = map(goal_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
|
||||
|
||||
// Draw the band and goal
|
||||
colour_band(strip_band_position, BAND_WIDTH, strip_goal_position, GOAL_WIDTH, hue);
|
||||
|
||||
// Check if the band is within the goal, and if so, set a new goal
|
||||
if(band_position >= goal_position - GOAL_MARGIN && band_position <= goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
if(band_position >= NUM_LEDS && band_position + NUM_LEDS < goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
if(goal_position - GOAL_MARGIN < 0 && band_position - NUM_LEDS >= goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
set(OUTPUT_NAME encoderwheel_clock)
|
||||
add_executable(${OUTPUT_NAME} clock.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
hardware_rtc
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue