├── .gitignore ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── demo.gif ├── docs ├── ST7789V_v1.6.pdf └── nRF52832_PS_v1.4.pdf ├── jlink.ocd ├── jlinkgdbserver.sh ├── openocd.sh └── pinetime-rtic ├── .cargo └── config ├── .embed.toml ├── .gdbinit ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── ferris.raw ├── memory.x └── src ├── backlight.rs ├── battery.rs ├── delay.rs ├── main.rs └── monotonic_nrf52.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2020-2021 Danilo Bargen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust/RTIC on PineTime 2 | 3 | Target MCU: nRF52832 (xxAA) 4 | 5 | Current status: PoC 6 | 7 | ![img](demo.gif) 8 | 9 | What works: 10 | 11 | - Bare-metal Rust with [nrf52-hal](https://github.com/nrf-rs/nrf-hal) 12 | - [RTIC](https://rtic.rs/) for concurrency 13 | - [embedded-graphics](https://github.com/jamwaffles/embedded-graphics) for drawing onto the LCD 14 | - Detect button presses 15 | - Cycle through backlight brightness levels using button 16 | - Show battery charge status and voltage 17 | - Send BLE advertisement frames using the pure-Rust 18 | [rubble](https://github.com/jonas-schievink/rubble) stack 19 | 20 | Planned: 21 | 22 | - A simple watch interface 23 | - Support for the step counter 24 | - Better Bluetooth support 25 | 26 | 27 | ## Development 28 | 29 | ### Flashing (cargo-embed) 30 | 31 | Install cargo-embed: 32 | 33 | $ cargo install -f --git https://github.com/probe-rs/cargo-embed/ 34 | 35 | Flash the target: 36 | 37 | $ cargo embed --release 38 | 39 | ### Flashing (openocd) 40 | 41 | Run OpenOCD: 42 | 43 | $ ./openocd.sh 44 | 45 | Run the code 46 | 47 | $ cargo run [--release] 48 | 49 | ### Flashing (J-Link GDB Server) 50 | 51 | Run JLinkGDBServer: 52 | 53 | $ ./jlinkgdbserver.sh 54 | 55 | Run the code 56 | 57 | $ cargo run [--release] 58 | 59 | 60 | ## License 61 | 62 | Licensed under either of 63 | 64 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 65 | http://www.apache.org/licenses/LICENSE-2.0) 66 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 67 | http://opensource.org/licenses/MIT) at your option. 68 | 69 | ### Contributing 70 | 71 | Unless you explicitly state otherwise, any contribution intentionally submitted 72 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 73 | be dual licensed as above, without any additional terms or conditions. 74 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbrgn/pinetime-rtic/4e111c92cd15abf62579c0e532df16517ea65720/demo.gif -------------------------------------------------------------------------------- /docs/ST7789V_v1.6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbrgn/pinetime-rtic/4e111c92cd15abf62579c0e532df16517ea65720/docs/ST7789V_v1.6.pdf -------------------------------------------------------------------------------- /docs/nRF52832_PS_v1.4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbrgn/pinetime-rtic/4e111c92cd15abf62579c0e532df16517ea65720/docs/nRF52832_PS_v1.4.pdf -------------------------------------------------------------------------------- /jlink.ocd: -------------------------------------------------------------------------------- 1 | interface jlink 2 | transport select swd 3 | source [find target/nrf52.cfg] 4 | -------------------------------------------------------------------------------- /jlinkgdbserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | JLinkGDBServerExe \ 5 | -select USB \ 6 | -device nRF52832_xxAA \ 7 | -endian little \ 8 | -if SWD \ 9 | -speed 4000 \ 10 | -port 3333 \ 11 | -noir \ 12 | -LocalhostOnly 13 | -------------------------------------------------------------------------------- /openocd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | openocd -f jlink.ocd -c 'init; reset; halt' 5 | -------------------------------------------------------------------------------- /pinetime-rtic/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | runner = "arm-none-eabi-gdb" 3 | rustflags = ["-C", "link-arg=-Tlink.x"] 4 | 5 | [build] 6 | target = "thumbv7em-none-eabihf" 7 | 8 | [test] 9 | target = "x86_64-unknown-linux-gnu" 10 | -------------------------------------------------------------------------------- /pinetime-rtic/.embed.toml: -------------------------------------------------------------------------------- 1 | [probe] 2 | protocol = "Swd" 3 | 4 | [flashing] 5 | enabled = true 6 | restore_unwritten_bytes = false 7 | 8 | [general] 9 | chip = "nrf52832_xxAA" 10 | -------------------------------------------------------------------------------- /pinetime-rtic/.gdbinit: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | # General config 4 | set backtrace limit 32 5 | 6 | # Target config 7 | #set arm force-mode thumb 8 | #monitor arm semihosting enable 9 | 10 | # Load binary 11 | load 12 | break main 13 | continue 14 | step 15 | -------------------------------------------------------------------------------- /pinetime-rtic/.gitignore: -------------------------------------------------------------------------------- 1 | .embed.local.toml 2 | -------------------------------------------------------------------------------- /pinetime-rtic/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aligned" 5 | version = "0.3.2" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "as-slice" 13 | version = "0.1.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "autocfg" 23 | version = "1.0.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "bare-metal" 28 | version = "0.2.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 32 | ] 33 | 34 | [[package]] 35 | name = "bitflags" 36 | version = "1.2.1" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | 39 | [[package]] 40 | name = "byteorder" 41 | version = "1.3.4" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | 44 | [[package]] 45 | name = "cast" 46 | version = "0.2.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | dependencies = [ 49 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 50 | ] 51 | 52 | [[package]] 53 | name = "cortex-m" 54 | version = "0.6.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | dependencies = [ 57 | "aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "cortex-m-rt" 64 | version = "0.6.12" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | dependencies = [ 67 | "cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "cortex-m-rt-macros" 73 | version = "0.1.8" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | dependencies = [ 76 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 79 | ] 80 | 81 | [[package]] 82 | name = "cortex-m-rtic" 83 | version = "0.5.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | dependencies = [ 86 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 88 | "cortex-m-rtic-macros 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "rtic-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "cortex-m-rtic-macros" 96 | version = "0.5.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "rtic-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 103 | ] 104 | 105 | [[package]] 106 | name = "debouncr" 107 | version = "0.1.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | dependencies = [ 110 | "doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 111 | ] 112 | 113 | [[package]] 114 | name = "doc-comment" 115 | version = "0.3.3" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | 118 | [[package]] 119 | name = "embedded-graphics" 120 | version = "0.6.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 124 | ] 125 | 126 | [[package]] 127 | name = "embedded-hal" 128 | version = "0.2.3" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | dependencies = [ 131 | "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 132 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "fpa" 137 | version = "0.1.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | dependencies = [ 140 | "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 141 | "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 142 | ] 143 | 144 | [[package]] 145 | name = "generic-array" 146 | version = "0.12.3" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | dependencies = [ 149 | "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 150 | ] 151 | 152 | [[package]] 153 | name = "generic-array" 154 | version = "0.13.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | dependencies = [ 157 | "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 158 | ] 159 | 160 | [[package]] 161 | name = "hash32" 162 | version = "0.1.1" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | dependencies = [ 165 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 166 | ] 167 | 168 | [[package]] 169 | name = "heapless" 170 | version = "0.5.4" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | dependencies = [ 173 | "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "indexmap" 181 | version = "1.3.2" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | dependencies = [ 184 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "nb" 189 | version = "0.1.2" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | 192 | [[package]] 193 | name = "nrf-hal-common" 194 | version = "0.10.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | dependencies = [ 197 | "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 198 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 199 | "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 200 | "fpa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "nrf52832-pac 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 205 | ] 206 | 207 | [[package]] 208 | name = "nrf52832-hal" 209 | version = "0.10.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | dependencies = [ 212 | "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 213 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 214 | "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 216 | "nrf-hal-common 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "nrf52832-pac 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 218 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 219 | ] 220 | 221 | [[package]] 222 | name = "nrf52832-pac" 223 | version = "0.9.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | dependencies = [ 226 | "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 227 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 228 | "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 229 | "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 230 | ] 231 | 232 | [[package]] 233 | name = "num-derive" 234 | version = "0.3.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | dependencies = [ 237 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 238 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 239 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 240 | ] 241 | 242 | [[package]] 243 | name = "num-traits" 244 | version = "0.2.11" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | dependencies = [ 247 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 248 | ] 249 | 250 | [[package]] 251 | name = "numtoa" 252 | version = "0.2.3" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | 255 | [[package]] 256 | name = "panic-rtt-target" 257 | version = "0.1.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | dependencies = [ 260 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "rtt-target 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 262 | ] 263 | 264 | [[package]] 265 | name = "pinetime-rtic" 266 | version = "0.1.0" 267 | dependencies = [ 268 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 270 | "cortex-m-rtic 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "debouncr 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "embedded-graphics 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "nrf52832-hal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "numtoa 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "panic-rtt-target 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "rtt-target 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "rubble 0.0.3 (git+https://github.com/jonas-schievink/rubble)", 279 | "rubble-nrf5x 0.0.3 (git+https://github.com/jonas-schievink/rubble)", 280 | "st7789 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 281 | ] 282 | 283 | [[package]] 284 | name = "proc-macro2" 285 | version = "1.0.10" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | dependencies = [ 288 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 289 | ] 290 | 291 | [[package]] 292 | name = "quote" 293 | version = "1.0.3" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | dependencies = [ 296 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 297 | ] 298 | 299 | [[package]] 300 | name = "r0" 301 | version = "0.2.2" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | 304 | [[package]] 305 | name = "rand_core" 306 | version = "0.5.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | 309 | [[package]] 310 | name = "rtic-core" 311 | version = "0.3.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | 314 | [[package]] 315 | name = "rtic-syntax" 316 | version = "0.4.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | dependencies = [ 319 | "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 320 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 321 | "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", 322 | ] 323 | 324 | [[package]] 325 | name = "rtt-target" 326 | version = "0.1.1" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | dependencies = [ 329 | "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 330 | "ufmt-write 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 331 | "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 332 | ] 333 | 334 | [[package]] 335 | name = "rubble" 336 | version = "0.0.3" 337 | source = "git+https://github.com/jonas-schievink/rubble#78a91724355ef139b988a042b0c8da17a1f89a02" 338 | dependencies = [ 339 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 340 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 341 | "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 342 | "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 343 | ] 344 | 345 | [[package]] 346 | name = "rubble-nrf5x" 347 | version = "0.0.3" 348 | source = "git+https://github.com/jonas-schievink/rubble#78a91724355ef139b988a042b0c8da17a1f89a02" 349 | dependencies = [ 350 | "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 351 | "nrf52832-hal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 352 | "rubble 0.0.3 (git+https://github.com/jonas-schievink/rubble)", 353 | ] 354 | 355 | [[package]] 356 | name = "rustc_version" 357 | version = "0.2.3" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | dependencies = [ 360 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 361 | ] 362 | 363 | [[package]] 364 | name = "semver" 365 | version = "0.9.0" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | dependencies = [ 368 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 369 | ] 370 | 371 | [[package]] 372 | name = "semver-parser" 373 | version = "0.7.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | 376 | [[package]] 377 | name = "st7789" 378 | version = "0.2.2" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "embedded-graphics 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 385 | "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 386 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 387 | ] 388 | 389 | [[package]] 390 | name = "stable_deref_trait" 391 | version = "1.1.1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | 394 | [[package]] 395 | name = "syn" 396 | version = "1.0.17" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | dependencies = [ 399 | "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", 400 | "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 401 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 402 | ] 403 | 404 | [[package]] 405 | name = "typenum" 406 | version = "1.12.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | 409 | [[package]] 410 | name = "ufmt-write" 411 | version = "0.1.0" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | 414 | [[package]] 415 | name = "unicode-xid" 416 | version = "0.2.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | 419 | [[package]] 420 | name = "uuid" 421 | version = "0.8.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | 424 | [[package]] 425 | name = "vcell" 426 | version = "0.1.2" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | 429 | [[package]] 430 | name = "version_check" 431 | version = "0.9.2" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | 434 | [[package]] 435 | name = "void" 436 | version = "1.0.2" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | 439 | [[package]] 440 | name = "volatile-register" 441 | version = "0.2.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | dependencies = [ 444 | "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 445 | ] 446 | 447 | [metadata] 448 | "checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" 449 | "checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" 450 | "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 451 | "checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 452 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 453 | "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 454 | "checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" 455 | "checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" 456 | "checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" 457 | "checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" 458 | "checksum cortex-m-rtic 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b30efcb6b7920d9016182c485687f0012487032a14c415d2fce6e9862ef8260e" 459 | "checksum cortex-m-rtic-macros 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9a1a6a4c9550373038c0e21a78d44d529bd697c25bbf6b8004bddc6e63b119c7" 460 | "checksum debouncr 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7aac9a92b2d575b05014b46dfe07da25d54662e7f1717c8bde82272937d75443" 461 | "checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 462 | "checksum embedded-graphics 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "619ef7a30b5368c6350a9edb41c628499a0a77072d1bfa949044473d23734e3c" 463 | "checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" 464 | "checksum fpa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f074479d683e5a8fd0bf1251d0a5d91b0d9178b867b44962191ed0eaaf8d4009" 465 | "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 466 | "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" 467 | "checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" 468 | "checksum heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ffa511365b12346c5fbe759d82f80d3aa70d9f1ba01955594f84a1a6bbab985" 469 | "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 470 | "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" 471 | "checksum nrf-hal-common 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc36b48f37fdeeb88821733f8049bfee490fa76376746760c83bc4faa850320b" 472 | "checksum nrf52832-hal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9656eab0c535fb4429876525fb5db1604bceeed80cc91a7b91d7b683db4a3a0" 473 | "checksum nrf52832-pac 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72920484274fae0792a40345049da2723612465c7202561b6a17ad3c127259db" 474 | "checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" 475 | "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 476 | "checksum numtoa 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e521b6adefa0b2c1fa5d2abdf9a5216288686fe6146249215d884c0e5ab320b0" 477 | "checksum panic-rtt-target 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3dec5d8d834d63b28db55b9f0d4fd3781599527217c865cd79349c71c8deb4" 478 | "checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 479 | "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 480 | "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" 481 | "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 482 | "checksum rtic-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab51fe832317e805f869b3d859f91aadf855c2c3da51f9b84bc645c201597158" 483 | "checksum rtic-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8152fcaa845720d61e6cc570548b89144c2c307f18a480bbd97e55e9f6eeff04" 484 | "checksum rtt-target 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "26ebda2070a2dd2f5d1bba0c427055f475bb011ca8215add472b6e6ba6b7265c" 485 | "checksum rubble 0.0.3 (git+https://github.com/jonas-schievink/rubble)" = "" 486 | "checksum rubble-nrf5x 0.0.3 (git+https://github.com/jonas-schievink/rubble)" = "" 487 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 488 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 489 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 490 | "checksum st7789 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2010ec486c3af0e3afa17517bd634d777b9fc4dfd6804c95dddc840cf425626d" 491 | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 492 | "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 493 | "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 494 | "checksum ufmt-write 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 495 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 496 | "checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" 497 | "checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" 498 | "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 499 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 500 | "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" 501 | -------------------------------------------------------------------------------- /pinetime-rtic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pinetime-rtic" 3 | version = "0.1.0" 4 | authors = ["Danilo Bargen "] 5 | edition = "2018" 6 | license = "MIT OR Apache-2.0" 7 | readme = "README.md" 8 | repository = "https://github.com/dbrgn/pinetime-rtic/" 9 | 10 | [dependencies] 11 | cortex-m = "0.6" 12 | cortex-m-rt = "0.6" 13 | cortex-m-rtic = "0.5.1" 14 | debouncr = "0.1.2" 15 | embedded-graphics = "0.6" 16 | embedded-hal = "0.2" 17 | nrf52832-hal = { version = "0.10", features = ["rt"], default-features = false } 18 | numtoa = "0.2" 19 | panic-rtt-target = { version = "0.1", features = ["cortex-m"] } 20 | rtt-target = { version = "0.1", features = ["cortex-m"] } 21 | rubble = { git = "https://github.com/jonas-schievink/rubble" } 22 | rubble-nrf5x = { git = "https://github.com/jonas-schievink/rubble", features = ["52832"], default-features = false } 23 | st7789 = { version = "0.2", features = ["graphics", "batch", "buffer"], default-features = false } 24 | 25 | [profile.dev] 26 | codegen-units = 1 27 | 28 | [profile.release] 29 | lto = true 30 | debug = true 31 | codegen-units = 1 32 | -------------------------------------------------------------------------------- /pinetime-rtic/ferris.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbrgn/pinetime-rtic/4e111c92cd15abf62579c0e532df16517ea65720/pinetime-rtic/ferris.raw -------------------------------------------------------------------------------- /pinetime-rtic/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 64K 6 | } 7 | -------------------------------------------------------------------------------- /pinetime-rtic/src/backlight.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::v2::OutputPin; 2 | use nrf52832_hal::gpio::{Output, Pin, PushPull}; 3 | use rtt_target::rprintln; 4 | 5 | /// Control the backlight. 6 | /// 7 | /// There are three active-low backlight pins, each connected to a FET that 8 | /// toggles backlight power through a resistor. 9 | /// 10 | /// - Low: 2.2 kΩ 11 | /// - Mid: 100 Ω 12 | /// - High: 30 Ω 13 | /// 14 | /// Through combinations of these pins, 7 brightness levels (+ off) can be 15 | /// configured. 16 | pub struct Backlight { 17 | low: Pin>, 18 | mid: Pin>, 19 | high: Pin>, 20 | 21 | /// The current brightness level (value between 0 and 7). 22 | brightness: u8, 23 | } 24 | 25 | impl Backlight { 26 | /// Initialize the backlight with the specified level (0–7). 27 | pub fn init( 28 | low: Pin>, 29 | mid: Pin>, 30 | high: Pin>, 31 | brightness: u8, 32 | ) -> Self { 33 | let mut backlight = Self { 34 | low, 35 | mid, 36 | high, 37 | brightness, 38 | }; 39 | backlight.set(brightness); 40 | backlight 41 | } 42 | 43 | /// Set the brightness level. Must be a value between 0 (off) and 7 (max 44 | /// brightness). Higher values are clamped to 7. 45 | pub fn set(&mut self, mut brightness: u8) { 46 | if brightness > 7 { 47 | brightness = 7; 48 | } 49 | rprintln!("Setting backlight brightness to {}", brightness); 50 | if brightness & 0x01 > 0 { 51 | self.low.set_low().unwrap(); 52 | } else { 53 | self.low.set_high().unwrap(); 54 | } 55 | if brightness & 0x02 > 0 { 56 | self.mid.set_low().unwrap(); 57 | } else { 58 | self.mid.set_high().unwrap(); 59 | } 60 | if brightness & 0x04 > 0 { 61 | self.high.set_low().unwrap(); 62 | } else { 63 | self.high.set_high().unwrap(); 64 | } 65 | self.brightness = brightness; 66 | } 67 | 68 | /// Turn off the backlight. 69 | pub fn off(&mut self) { 70 | self.set(0); 71 | } 72 | 73 | /// Increase backlight brightness. 74 | pub fn brighter(&mut self) { 75 | self.set(self.brightness + 1); 76 | } 77 | 78 | /// Decrease backlight brightness. 79 | pub fn darker(&mut self) { 80 | self.set(self.brightness - 1); 81 | } 82 | 83 | /// Return the current brightness level (value between 0 and 7). 84 | pub fn get_brightness(&self) -> u8 { 85 | self.brightness 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /pinetime-rtic/src/battery.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::adc::OneShot; 2 | use embedded_hal::digital::v2::InputPin; 3 | use nrf52832_hal::gpio::{p0, Floating, Input}; 4 | use nrf52832_hal::saadc::{Saadc, SaadcConfig}; 5 | use nrf52832_hal::target::SAADC; 6 | 7 | pub struct BatteryStatus { 8 | /// Pin P0.12: High = battery, Low = charging. 9 | pin_charge_indication: p0::P0_12>, 10 | 11 | /// Pin P0.31: Voltage level 12 | pin_voltage: p0::P0_31>, 13 | 14 | /// SAADC peripheral 15 | saadc: Saadc, 16 | 17 | /// Charging state 18 | charging: bool, 19 | 20 | /// Battery voltage in 0.1 volts 21 | voltage: u8, 22 | } 23 | 24 | impl BatteryStatus { 25 | /// Initialize the battery status. 26 | pub fn init( 27 | pin_charge_indication: p0::P0_12>, 28 | mut pin_voltage: p0::P0_31>, 29 | #[allow(non_snake_case)] SAADC: SAADC, 30 | ) -> Self { 31 | // Get initial charging state 32 | let charging = pin_charge_indication.is_low().unwrap(); 33 | 34 | // Get initial voltage 35 | let mut saadc = Saadc::new(SAADC, SaadcConfig::default()); 36 | let voltage = 37 | Self::convert_adc_measurement(saadc.read(&mut pin_voltage).unwrap()).unwrap_or(0); 38 | 39 | Self { 40 | pin_charge_indication, 41 | pin_voltage, 42 | saadc, 43 | charging, 44 | voltage, 45 | } 46 | } 47 | 48 | /// Convert a raw ADC measurement into a battery voltage in 0.1 volts. 49 | fn convert_adc_measurement(raw_measurement: i16) -> Option { 50 | if raw_measurement < 0 { 51 | // What? 52 | return None; 53 | } 54 | let adc_val: u32 = (raw_measurement as u16).into(); // keep as 32bit for multiplication 55 | let battery_voltage: u32 = (adc_val * 2000) / 4965; // we multiply the ADC value by 2 * 1000 for mV and divide by (2 ^ 14 / 3.3V reference) 56 | Some((battery_voltage / 100) as u8) 57 | } 58 | 59 | /// Return whether the watch is currently charging. 60 | /// 61 | /// This returns the stored value. To fetch current data, call `update()` first. 62 | pub fn is_charging(&self) -> bool { 63 | self.charging 64 | } 65 | 66 | /// Return the current battery charge in percent (0–100). 67 | /// 68 | /// This returns the stored value. To fetch current data, call `update()` first. 69 | pub fn percent(&self) -> u8 { 70 | unimplemented!(); 71 | } 72 | 73 | /// Return the current battery voltage in 0.1 volts. 74 | /// 75 | /// This returns the stored value. To fetch current data, call `update()` first. 76 | pub fn voltage(&self) -> u8 { 77 | self.voltage 78 | } 79 | 80 | /// Update the current battery status by reading information from the 81 | /// hardware. Return whether or not the values changed. 82 | pub fn update(&mut self) -> bool { 83 | let mut changed = false; 84 | 85 | // Check charging status 86 | let charging = self.pin_charge_indication.is_low().unwrap(); 87 | if charging != self.charging { 88 | self.charging = charging; 89 | changed = true; 90 | } 91 | 92 | // Check voltage 93 | let voltage = 94 | Self::convert_adc_measurement(self.saadc.read(&mut self.pin_voltage).unwrap()) 95 | .unwrap_or(0); 96 | if voltage != self.voltage { 97 | self.voltage = voltage; 98 | changed = true; 99 | } 100 | 101 | changed 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /pinetime-rtic/src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delay implementation using regular timers. 2 | //! 3 | //! This is done because RTIC takes ownership of SYST, and the nrf52-hal by 4 | //! default also wants SYST for its Delay implementation. 5 | 6 | use embedded_hal::blocking::delay::DelayUs; 7 | use nrf52832_hal::{ 8 | self as hal, 9 | pac, 10 | timer::Timer, 11 | }; 12 | 13 | pub struct TimerDelay { 14 | timer: hal::Timer, 15 | } 16 | 17 | impl TimerDelay { 18 | pub fn new(timer0: pac::TIMER0) -> Self { 19 | Self { 20 | timer: Timer::new(timer0), 21 | } 22 | } 23 | } 24 | 25 | impl DelayUs for TimerDelay { 26 | fn delay_us(&mut self, us: u32) { 27 | // Currently the HAL timer is hardcoded at 1 MHz, 28 | // so 1 cycle = 1 µs. 29 | let cycles = us; 30 | self.timer.delay(cycles); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pinetime-rtic/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![cfg_attr(not(test), no_std)] 3 | 4 | // Panic handler 5 | #[cfg(not(test))] 6 | use panic_rtt_target as _; 7 | 8 | use debouncr::{debounce_6, Debouncer, Edge, Repeat6}; 9 | use embedded_graphics::prelude::*; 10 | use embedded_graphics::{ 11 | fonts::{Font12x16, Text}, 12 | image::{Image, ImageRawLE}, 13 | pixelcolor::Rgb565, 14 | primitives::rectangle::Rectangle, 15 | style::{PrimitiveStyleBuilder, TextStyleBuilder}, 16 | }; 17 | use nrf52832_hal::gpio::{p0, Floating, Input, Level, Output, Pin, PushPull}; 18 | use nrf52832_hal::prelude::*; 19 | use nrf52832_hal::{self as hal, pac}; 20 | use numtoa::NumToA; 21 | use rtic::app; 22 | use rtt_target::{rprintln, rtt_init_print}; 23 | use rubble::config::Config; 24 | use rubble::gatt::BatteryServiceAttrs; 25 | use rubble::l2cap::{BleChannelMap, L2CAPState}; 26 | use rubble::link::ad_structure::AdStructure; 27 | use rubble::link::queue::{PacketQueue, SimpleQueue}; 28 | use rubble::link::{LinkLayer, Responder, MIN_PDU_BUF}; 29 | use rubble::security::NoSecurity; 30 | use rubble::time::{Duration as RubbleDuration, Timer}; 31 | use rubble_nrf5x::radio::{BleRadio, PacketBuffer}; 32 | use rubble_nrf5x::timer::BleTimer; 33 | use rubble_nrf5x::utils::get_device_address; 34 | use st7789::{self, Orientation}; 35 | 36 | mod backlight; 37 | mod battery; 38 | mod delay; 39 | mod monotonic_nrf52; 40 | 41 | use monotonic_nrf52::U32Ext; 42 | 43 | const LCD_W: u16 = 240; 44 | const LCD_H: u16 = 240; 45 | 46 | const FERRIS_W: u16 = 86; 47 | const FERRIS_H: u16 = 64; 48 | 49 | const MARGIN: u16 = 10; 50 | 51 | const BACKGROUND_COLOR: Rgb565 = Rgb565::new(0, 0b000111, 0); 52 | 53 | pub struct AppConfig {} 54 | 55 | impl Config for AppConfig { 56 | type Timer = BleTimer; 57 | type Transmitter = BleRadio; 58 | type ChannelMapper = BleChannelMap; 59 | type PacketQueue = &'static mut SimpleQueue; 60 | } 61 | 62 | #[app(device = nrf52832_hal::pac, peripherals = true, monotonic = crate::monotonic_nrf52::Tim1)] 63 | const APP: () = { 64 | struct Resources { 65 | // LCD 66 | lcd: st7789::ST7789< 67 | hal::spim::Spim, 68 | p0::P0_18>, 69 | p0::P0_26>, 70 | delay::TimerDelay, 71 | >, 72 | backlight: backlight::Backlight, 73 | 74 | // Battery 75 | battery: battery::BatteryStatus, 76 | 77 | // Button 78 | button: Pin>, 79 | button_debouncer: Debouncer, 80 | 81 | // Styles 82 | text_style: TextStyleBuilder, 83 | 84 | // Counter resources 85 | #[init(0)] 86 | counter: usize, 87 | 88 | // Ferris resources 89 | ferris: ImageRawLE<'static, Rgb565>, 90 | #[init(10)] 91 | ferris_x_offset: i32, 92 | #[init(80)] 93 | ferris_y_offset: i32, 94 | #[init(2)] 95 | ferris_step_size: i32, 96 | 97 | // BLE 98 | #[init([0; MIN_PDU_BUF])] 99 | ble_tx_buf: PacketBuffer, 100 | #[init([0; MIN_PDU_BUF])] 101 | ble_rx_buf: PacketBuffer, 102 | #[init(SimpleQueue::new())] 103 | tx_queue: SimpleQueue, 104 | #[init(SimpleQueue::new())] 105 | rx_queue: SimpleQueue, 106 | radio: BleRadio, 107 | ble_ll: LinkLayer, 108 | ble_r: Responder, 109 | } 110 | 111 | #[init( 112 | resources = [ble_tx_buf, ble_rx_buf, tx_queue, rx_queue], 113 | spawn = [write_counter, write_ferris, poll_button, show_battery_status, update_battery_status], 114 | )] 115 | fn init(cx: init::Context) -> init::LateResources { 116 | // Destructure device peripherals 117 | let pac::Peripherals { 118 | CLOCK, 119 | FICR, 120 | P0, 121 | RADIO, 122 | SAADC, 123 | SPIM1, 124 | TIMER0, 125 | TIMER1, 126 | TIMER2, 127 | .. 128 | } = cx.device; 129 | 130 | // Init RTT 131 | rtt_init_print!(); 132 | rprintln!("Initializing…"); 133 | 134 | // Set up clocks. On reset, the high frequency clock is already used, 135 | // but we also need to switch to the external HF oscillator. This is 136 | // needed for Bluetooth to work. 137 | let _clocks = hal::clocks::Clocks::new(CLOCK).enable_ext_hfosc(); 138 | 139 | // Set up delay provider on TIMER0 140 | let delay = delay::TimerDelay::new(TIMER0); 141 | 142 | // Initialize monotonic timer on TIMER1 (for RTIC) 143 | monotonic_nrf52::Tim1::initialize(TIMER1); 144 | 145 | // Initialize BLE timer on TIMER2 146 | let ble_timer = BleTimer::init(TIMER2); 147 | 148 | // Set up GPIO peripheral 149 | let gpio = hal::gpio::p0::Parts::new(P0); 150 | 151 | // Enable backlight 152 | let backlight = backlight::Backlight::init( 153 | gpio.p0_14.into_push_pull_output(Level::High).degrade(), 154 | gpio.p0_22.into_push_pull_output(Level::High).degrade(), 155 | gpio.p0_23.into_push_pull_output(Level::High).degrade(), 156 | 1, 157 | ); 158 | 159 | // Battery status 160 | let battery = battery::BatteryStatus::init( 161 | gpio.p0_12.into_floating_input(), 162 | gpio.p0_31.into_floating_input(), 163 | SAADC, 164 | ); 165 | 166 | // Enable button 167 | gpio.p0_15.into_push_pull_output(Level::High); 168 | let button = gpio.p0_13.into_floating_input().degrade(); 169 | 170 | // Get bluetooth device address 171 | let device_address = get_device_address(); 172 | rprintln!("Bluetooth device address: {:?}", device_address); 173 | 174 | // Initialize radio 175 | let mut radio = BleRadio::new( 176 | RADIO, 177 | &FICR, 178 | cx.resources.ble_tx_buf, 179 | cx.resources.ble_rx_buf, 180 | ); 181 | 182 | // Create bluetooth TX/RX queues 183 | let (tx, tx_cons) = cx.resources.tx_queue.split(); 184 | let (rx_prod, rx) = cx.resources.rx_queue.split(); 185 | 186 | // Create the actual BLE stack objects 187 | let mut ble_ll = LinkLayer::::new(device_address, ble_timer); 188 | let ble_r = Responder::::new( 189 | tx, 190 | rx, 191 | L2CAPState::new(BleChannelMap::with_attributes(BatteryServiceAttrs::new())), 192 | ); 193 | 194 | // Send advertisement and set up regular interrupt 195 | let next_update = ble_ll 196 | .start_advertise( 197 | RubbleDuration::from_millis(200), 198 | &[AdStructure::CompleteLocalName("Rusty PineTime")], 199 | &mut radio, 200 | tx_cons, 201 | rx_prod, 202 | ) 203 | .unwrap(); 204 | ble_ll.timer().configure_interrupt(next_update); 205 | 206 | // Set up SPI pins 207 | let spi_clk = gpio.p0_02.into_push_pull_output(Level::Low).degrade(); 208 | let spi_mosi = gpio.p0_03.into_push_pull_output(Level::Low).degrade(); 209 | let spi_miso = gpio.p0_04.into_floating_input().degrade(); 210 | let spi_pins = hal::spim::Pins { 211 | sck: spi_clk, 212 | miso: Some(spi_miso), 213 | mosi: Some(spi_mosi), 214 | }; 215 | 216 | // Set up LCD pins 217 | // LCD_CS (P0.25): Chip select 218 | let mut lcd_cs = gpio.p0_25.into_push_pull_output(Level::Low); 219 | // LCD_RS (P0.18): Data/clock pin 220 | let lcd_dc = gpio.p0_18.into_push_pull_output(Level::Low); 221 | // LCD_RESET (P0.26): Display reset 222 | let lcd_rst = gpio.p0_26.into_push_pull_output(Level::Low); 223 | 224 | // Initialize SPI 225 | let spi = hal::Spim::new( 226 | SPIM1, 227 | spi_pins, 228 | // Use SPI at 8MHz (the fastest clock available on the nRF52832) 229 | // because otherwise refreshing will be super slow. 230 | hal::spim::Frequency::M8, 231 | // SPI must be used in mode 3. Mode 0 (the default) won't work. 232 | hal::spim::MODE_3, 233 | 0, 234 | ); 235 | 236 | // Chip select must be held low while driving the display. It must be high 237 | // when using other SPI devices on the same bus (such as external flash 238 | // storage) so that the display controller won't respond to the wrong 239 | // commands. 240 | lcd_cs.set_low().unwrap(); 241 | 242 | // Initialize LCD 243 | let mut lcd = st7789::ST7789::new(spi, lcd_dc, lcd_rst, LCD_W, LCD_H, delay); 244 | lcd.init().unwrap(); 245 | lcd.set_orientation(&Orientation::Portrait).unwrap(); 246 | 247 | // Draw something onto the LCD 248 | let backdrop_style = PrimitiveStyleBuilder::new() 249 | .fill_color(BACKGROUND_COLOR) 250 | .build(); 251 | Rectangle::new(Point::new(0, 0), Point::new(LCD_W as i32, LCD_H as i32)) 252 | .into_styled(backdrop_style) 253 | .draw(&mut lcd) 254 | .unwrap(); 255 | 256 | // Choose text style 257 | let text_style = TextStyleBuilder::new(Font12x16) 258 | .text_color(Rgb565::WHITE) 259 | .background_color(BACKGROUND_COLOR); 260 | 261 | // Draw text 262 | Text::new("PineTime", Point::new(10, 10)) 263 | .into_styled(text_style.build()) 264 | .draw(&mut lcd) 265 | .unwrap(); 266 | 267 | // Load ferris image data 268 | let ferris = ImageRawLE::new( 269 | include_bytes!("../ferris.raw"), 270 | FERRIS_W as u32, 271 | FERRIS_H as u32, 272 | ); 273 | 274 | // Schedule tasks immediately 275 | cx.spawn.write_counter().unwrap(); 276 | cx.spawn.write_ferris().unwrap(); 277 | cx.spawn.poll_button().unwrap(); 278 | cx.spawn.show_battery_status().unwrap(); 279 | cx.spawn.update_battery_status().unwrap(); 280 | 281 | init::LateResources { 282 | lcd, 283 | battery, 284 | backlight, 285 | button, 286 | button_debouncer: debounce_6(), 287 | text_style, 288 | ferris, 289 | 290 | radio, 291 | ble_ll, 292 | ble_r, 293 | } 294 | } 295 | 296 | /// Hook up the RADIO interrupt to the Rubble BLE stack. 297 | #[task(binds = RADIO, resources = [radio, ble_ll], spawn = [ble_worker], priority = 3)] 298 | fn radio(cx: radio::Context) { 299 | let ble_ll: &mut LinkLayer = cx.resources.ble_ll; 300 | if let Some(cmd) = cx 301 | .resources 302 | .radio 303 | .recv_interrupt(ble_ll.timer().now(), ble_ll) 304 | { 305 | cx.resources.radio.configure_receiver(cmd.radio); 306 | ble_ll.timer().configure_interrupt(cmd.next_update); 307 | 308 | if cmd.queued_work { 309 | // If there's any lower-priority work to be done, ensure that happens. 310 | // If we fail to spawn the task, it's already scheduled. 311 | cx.spawn.ble_worker().ok(); 312 | } 313 | } 314 | } 315 | 316 | /// Hook up the TIMER2 interrupt to the Rubble BLE stack. 317 | #[task(binds = TIMER2, resources = [radio, ble_ll], spawn = [ble_worker], priority = 3)] 318 | fn timer2(cx: timer2::Context) { 319 | let timer = cx.resources.ble_ll.timer(); 320 | if !timer.is_interrupt_pending() { 321 | return; 322 | } 323 | timer.clear_interrupt(); 324 | 325 | let cmd = cx.resources.ble_ll.update_timer(&mut *cx.resources.radio); 326 | cx.resources.radio.configure_receiver(cmd.radio); 327 | 328 | cx.resources 329 | .ble_ll 330 | .timer() 331 | .configure_interrupt(cmd.next_update); 332 | 333 | if cmd.queued_work { 334 | // If there's any lower-priority work to be done, ensure that happens. 335 | // If we fail to spawn the task, it's already scheduled. 336 | cx.spawn.ble_worker().ok(); 337 | } 338 | } 339 | 340 | /// Lower-priority task spawned from RADIO and TIMER2 interrupts. 341 | #[task(resources = [ble_r], priority = 2)] 342 | fn ble_worker(cx: ble_worker::Context) { 343 | // Fully drain the packet queue 344 | while cx.resources.ble_r.has_work() { 345 | cx.resources.ble_r.process_one().unwrap(); 346 | } 347 | } 348 | 349 | #[task(resources = [lcd, ferris, ferris_x_offset, ferris_y_offset, ferris_step_size], schedule = [write_ferris])] 350 | fn write_ferris(cx: write_ferris::Context) { 351 | // Draw ferris 352 | Image::new( 353 | &cx.resources.ferris, 354 | Point::new(*cx.resources.ferris_x_offset, *cx.resources.ferris_y_offset), 355 | ) 356 | .draw(cx.resources.lcd) 357 | .unwrap(); 358 | 359 | // Clean up behind ferris 360 | let backdrop_style = PrimitiveStyleBuilder::new() 361 | .fill_color(BACKGROUND_COLOR) 362 | .build(); 363 | let (p1, p2) = if *cx.resources.ferris_step_size > 0 { 364 | // Clean up to the left 365 | ( 366 | Point::new( 367 | *cx.resources.ferris_x_offset - *cx.resources.ferris_step_size, 368 | *cx.resources.ferris_y_offset, 369 | ), 370 | Point::new( 371 | *cx.resources.ferris_x_offset, 372 | *cx.resources.ferris_y_offset + (FERRIS_H as i32), 373 | ), 374 | ) 375 | } else { 376 | // Clean up to the right 377 | ( 378 | Point::new( 379 | *cx.resources.ferris_x_offset + FERRIS_W as i32, 380 | *cx.resources.ferris_y_offset, 381 | ), 382 | Point::new( 383 | *cx.resources.ferris_x_offset + FERRIS_W as i32 384 | - *cx.resources.ferris_step_size, 385 | *cx.resources.ferris_y_offset + (FERRIS_H as i32), 386 | ), 387 | ) 388 | }; 389 | Rectangle::new(p1, p2) 390 | .into_styled(backdrop_style) 391 | .draw(cx.resources.lcd) 392 | .unwrap(); 393 | 394 | // Reset step size 395 | if *cx.resources.ferris_x_offset as u16 > LCD_W - FERRIS_W - MARGIN { 396 | *cx.resources.ferris_step_size = -*cx.resources.ferris_step_size; 397 | } else if (*cx.resources.ferris_x_offset as u16) < MARGIN { 398 | *cx.resources.ferris_step_size = -*cx.resources.ferris_step_size; 399 | } 400 | *cx.resources.ferris_x_offset += *cx.resources.ferris_step_size; 401 | 402 | // Re-schedule the timer interrupt 403 | cx.schedule.write_ferris(cx.scheduled + 25.hz()).unwrap(); 404 | } 405 | 406 | #[task(resources = [lcd, text_style, counter], schedule = [write_counter])] 407 | fn write_counter(cx: write_counter::Context) { 408 | rprintln!("Counter is {}", cx.resources.counter); 409 | 410 | // Write counter to the display 411 | let mut buf = [0u8; 20]; 412 | let text = cx.resources.counter.numtoa_str(10, &mut buf); 413 | Text::new(text, Point::new(10, LCD_H as i32 - 10 - 16)) 414 | .into_styled(cx.resources.text_style.build()) 415 | .draw(cx.resources.lcd) 416 | .unwrap(); 417 | 418 | // Increment counter 419 | *cx.resources.counter += 1; 420 | 421 | // Re-schedule the timer interrupt 422 | cx.schedule.write_counter(cx.scheduled + 1.secs()).unwrap(); 423 | } 424 | 425 | #[task(resources = [button, button_debouncer], spawn = [button_pressed], schedule = [poll_button])] 426 | fn poll_button(cx: poll_button::Context) { 427 | // Poll button 428 | let pressed = cx.resources.button.is_high().unwrap(); 429 | let edge = cx.resources.button_debouncer.update(pressed); 430 | 431 | // Dispatch event 432 | if edge == Some(Edge::Rising) { 433 | cx.spawn.button_pressed().unwrap(); 434 | } 435 | 436 | // Re-schedule the timer interrupt in 2ms 437 | cx.schedule.poll_button(cx.scheduled + 2.millis()).unwrap(); 438 | } 439 | 440 | /// Called when button is pressed without bouncing for 12 (6 * 2) ms. 441 | #[task(resources = [backlight])] 442 | fn button_pressed(cx: button_pressed::Context) { 443 | if cx.resources.backlight.get_brightness() < 7 { 444 | cx.resources.backlight.brighter(); 445 | } else { 446 | cx.resources.backlight.off(); 447 | } 448 | } 449 | 450 | /// Fetch the battery status from the hardware. Update the text if 451 | /// something changed. 452 | #[task(resources = [battery], spawn = [show_battery_status], schedule = [update_battery_status])] 453 | fn update_battery_status(cx: update_battery_status::Context) { 454 | rprintln!("Update battery status"); 455 | 456 | let changed = cx.resources.battery.update(); 457 | if changed { 458 | rprintln!("Battery status changed"); 459 | cx.spawn.show_battery_status().unwrap(); 460 | } 461 | 462 | // Re-schedule the timer interrupt in 1s 463 | cx.schedule 464 | .update_battery_status(cx.scheduled + 1.secs()) 465 | .unwrap(); 466 | } 467 | 468 | /// Show the battery status on the LCD. 469 | #[task(resources = [battery, lcd, text_style])] 470 | fn show_battery_status(cx: show_battery_status::Context) { 471 | let voltage = cx.resources.battery.voltage(); 472 | let charging = cx.resources.battery.is_charging(); 473 | 474 | rprintln!( 475 | "Battery status: {} ({})", 476 | voltage, 477 | if charging { "charging" } else { "discharging" }, 478 | ); 479 | 480 | // Show battery status in top right corner 481 | let mut buf = [0u8; 6]; 482 | (voltage / 10).numtoa(10, &mut buf[0..1]); 483 | buf[1] = b'.'; 484 | (voltage % 10).numtoa(10, &mut buf[2..3]); 485 | buf[3] = b'V'; 486 | buf[4] = b'/'; 487 | buf[5] = if charging { b'C' } else { b'D' }; 488 | let status = core::str::from_utf8(&buf).unwrap(); 489 | let text = Text::new(status, Point::zero()).into_styled(cx.resources.text_style.build()); 490 | let translation = Point::new( 491 | LCD_W as i32 - text.size().width as i32 - MARGIN as i32, 492 | MARGIN as i32, 493 | ); 494 | text.translate(translation).draw(cx.resources.lcd).unwrap(); 495 | } 496 | 497 | // Provide unused interrupts to RTIC for its scheduling 498 | extern "C" { 499 | fn SWI0_EGU0(); 500 | fn SWI1_EGU1(); 501 | fn SWI2_EGU2(); 502 | fn SWI3_EGU3(); 503 | fn SWI4_EGU4(); 504 | fn SWI5_EGU5(); 505 | } 506 | }; 507 | -------------------------------------------------------------------------------- /pinetime-rtic/src/monotonic_nrf52.rs: -------------------------------------------------------------------------------- 1 | //! Using NRF52 as monotonic timer 2 | //! 3 | //! Source: 4 | //! https://github.com/rtic-rs/rtic-examples/blob/master/rtic_v5/monotonic_nrf52/src/monotonic_nrf52.rs 5 | 6 | use core::u32; 7 | use core::{ 8 | cmp::Ordering, 9 | convert::{Infallible, TryInto}, 10 | fmt, ops, 11 | }; 12 | use nrf52832_hal::target; 13 | use rtic::Monotonic; 14 | 15 | /// A measurement of the counter. Opaque and useful only with `Duration` 16 | /// 17 | /// # Correctness 18 | /// 19 | /// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively 20 | /// makes it "wrap around" and creates an incorrect value. This is also true if the operation is 21 | /// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks. 22 | #[derive(Clone, Copy, Eq, PartialEq)] 23 | pub struct Instant { 24 | inner: i32, 25 | } 26 | 27 | impl Instant { 28 | /// Returns an instant corresponding to "now" 29 | pub fn now() -> Self { 30 | let now = { 31 | let timer = unsafe { &*target::TIMER1::ptr() }; 32 | timer.tasks_capture[0].write(|w| unsafe { w.bits(1) }); 33 | timer.cc[0].read().bits() 34 | }; 35 | 36 | Instant { inner: now as i32 } 37 | } 38 | 39 | /// Returns the amount of time elapsed since this instant was created. 40 | pub fn elapsed(&self) -> Duration { 41 | Instant::now() - *self 42 | } 43 | 44 | /// Returns the underlying count 45 | pub fn counts(&self) -> u32 { 46 | self.inner as u32 47 | } 48 | 49 | /// Returns the amount of time elapsed from another instant to this one. 50 | pub fn duration_since(&self, earlier: Instant) -> Duration { 51 | let diff = self.inner - earlier.inner; 52 | assert!(diff >= 0, "second instant is later than self"); 53 | Duration { inner: diff as u32 } 54 | } 55 | } 56 | 57 | impl fmt::Debug for Instant { 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 59 | f.debug_tuple("Instant") 60 | .field(&(self.inner as u32)) 61 | .finish() 62 | } 63 | } 64 | 65 | impl ops::AddAssign for Instant { 66 | fn add_assign(&mut self, dur: Duration) { 67 | // NOTE this is a debug assertion because there's no foolproof way to detect a wrap around; 68 | // the user may write `(instant + dur) + dur` where `dur` is `(1<<31)-1` ticks. 69 | debug_assert!(dur.inner < (1 << 31)); 70 | self.inner = self.inner.wrapping_add(dur.inner as i32); 71 | } 72 | } 73 | 74 | impl ops::Add for Instant { 75 | type Output = Self; 76 | 77 | fn add(mut self, dur: Duration) -> Self { 78 | self += dur; 79 | self 80 | } 81 | } 82 | 83 | impl ops::SubAssign for Instant { 84 | fn sub_assign(&mut self, dur: Duration) { 85 | // NOTE see the NOTE in `>::add_assign` 86 | debug_assert!(dur.inner < (1 << 31)); 87 | self.inner = self.inner.wrapping_sub(dur.inner as i32); 88 | } 89 | } 90 | 91 | impl ops::Sub for Instant { 92 | type Output = Self; 93 | 94 | fn sub(mut self, dur: Duration) -> Self { 95 | self -= dur; 96 | self 97 | } 98 | } 99 | 100 | impl ops::Sub for Instant { 101 | type Output = Duration; 102 | 103 | fn sub(self, other: Instant) -> Duration { 104 | self.duration_since(other) 105 | } 106 | } 107 | 108 | impl Ord for Instant { 109 | fn cmp(&self, rhs: &Self) -> Ordering { 110 | self.inner.wrapping_sub(rhs.inner).cmp(&0) 111 | } 112 | } 113 | 114 | impl PartialOrd for Instant { 115 | fn partial_cmp(&self, rhs: &Self) -> Option { 116 | Some(self.cmp(rhs)) 117 | } 118 | } 119 | 120 | /// A `Duration` type to represent a span of time. 121 | /// 122 | /// This data type is only available on ARMv7-M 123 | /// 124 | /// # Correctness 125 | /// 126 | /// This type is *not* appropriate for representing time spans in the order of, or larger than, 127 | /// seconds because it can hold a maximum of `(1 << 31)` "ticks" where each tick is the inverse of 128 | /// the CPU frequency, which usually is dozens of MHz. 129 | #[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] 130 | pub struct Duration { 131 | inner: u32, 132 | } 133 | 134 | impl Duration { 135 | /// Creates a new `Duration` from the specified number of clock cycles 136 | pub fn from_cycles(cycles: u32) -> Self { 137 | Duration { inner: cycles } 138 | } 139 | 140 | /// Returns the total number of clock cycles contained by this `Duration` 141 | pub fn as_cycles(&self) -> u32 { 142 | self.inner 143 | } 144 | } 145 | 146 | // Used internally by RTIC to convert the duration into a known type 147 | impl TryInto for Duration { 148 | type Error = Infallible; 149 | 150 | fn try_into(self) -> Result { 151 | Ok(self.as_cycles()) 152 | } 153 | } 154 | 155 | impl ops::AddAssign for Duration { 156 | fn add_assign(&mut self, dur: Duration) { 157 | self.inner += dur.inner; 158 | } 159 | } 160 | 161 | impl ops::Add for Duration { 162 | type Output = Self; 163 | 164 | fn add(self, other: Self) -> Self { 165 | Duration { 166 | inner: self.inner + other.inner, 167 | } 168 | } 169 | } 170 | 171 | impl ops::Mul for Duration { 172 | type Output = Self; 173 | 174 | fn mul(self, other: u32) -> Self { 175 | Duration { 176 | inner: self.inner * other, 177 | } 178 | } 179 | } 180 | 181 | impl ops::MulAssign for Duration { 182 | fn mul_assign(&mut self, other: u32) { 183 | *self = *self * other; 184 | } 185 | } 186 | 187 | impl ops::SubAssign for Duration { 188 | fn sub_assign(&mut self, rhs: Duration) { 189 | self.inner -= rhs.inner; 190 | } 191 | } 192 | 193 | impl ops::Sub for Duration { 194 | type Output = Self; 195 | 196 | fn sub(self, rhs: Self) -> Self { 197 | Duration { 198 | inner: self.inner - rhs.inner, 199 | } 200 | } 201 | } 202 | 203 | /// Adds the `millis` and `micros` methods to the `u32` type 204 | /// 205 | /// This trait is only available on ARMv7-M 206 | pub trait U32Ext { 207 | /// Converts the `u32` value as seconds into ticks 208 | fn secs(self) -> Duration; 209 | 210 | /// Converts the `u32` value as milliseconds into ticks 211 | fn millis(self) -> Duration; 212 | 213 | /// Converts the `u32` value as microseconds into ticks 214 | fn micros(self) -> Duration; 215 | 216 | /// Converts the `u32` value as hertz into ticks 217 | fn hz(self) -> Duration; 218 | } 219 | 220 | impl U32Ext for u32 { 221 | fn secs(self) -> Duration { 222 | self.millis() * 1_000 223 | } 224 | 225 | fn millis(self) -> Duration { 226 | self.micros() * 1_000 227 | } 228 | 229 | fn micros(self) -> Duration { 230 | let frac = Tim1::ratio(); 231 | Duration { 232 | inner: (64 * frac.denominator * self) / frac.numerator, 233 | } 234 | } 235 | 236 | fn hz(self) -> Duration { 237 | (1_000_000 / self).micros() 238 | } 239 | } 240 | 241 | /// Implementor of the `rtic::Monotonic` traits and used to consume the timer 242 | /// to not allow for erroneous configuration. 243 | /// 244 | /// The timer must be initialized through `initialize()`. 245 | pub struct Tim1; 246 | 247 | impl Tim1 { 248 | pub fn initialize(timer: target::TIMER1) { 249 | // Auto restart, make sure the entire timer won't stop for any event 250 | timer.shorts.write(|w| { 251 | w.compare0_clear() 252 | .enabled() 253 | .compare0_stop() 254 | .disabled() 255 | .compare1_clear() 256 | .enabled() 257 | .compare1_stop() 258 | .disabled() 259 | .compare2_clear() 260 | .enabled() 261 | .compare2_stop() 262 | .disabled() 263 | .compare3_clear() 264 | .enabled() 265 | .compare3_stop() 266 | .disabled() 267 | }); 268 | 269 | // 1 MHz mode 270 | timer.prescaler.write(|w| unsafe { w.prescaler().bits(4) }); 271 | 272 | // 32 bit mode 273 | timer.bitmode.write(|w| w.bitmode()._32bit()); 274 | 275 | // Set compare value to max, not sure if this is needed 276 | timer.cc[0].write(|w| unsafe { w.cc().bits(u32::MAX) }); 277 | 278 | // Clear the counter value 279 | timer.tasks_clear.write(|w| unsafe { w.bits(1) }); 280 | 281 | // Start the timer 282 | timer.tasks_start.write(|w| unsafe { w.bits(1) }); 283 | 284 | // Throw away the timer, it is now setup and consumed 285 | drop(timer); 286 | } 287 | } 288 | 289 | impl rtic::Monotonic for Tim1 { 290 | type Instant = Instant; 291 | 292 | /// The ratio between the system timer (SysTick) frequency and this clock 293 | /// frequency, i.e. `Monotonic clock * Fraction = System clock`. 294 | fn ratio() -> rtic::Fraction { 295 | // The TIM1 timer is being initialized with 1 MHz. If we multiply by 64, 296 | // we get the sys clock of 64 MHz. 297 | rtic::Fraction { 298 | numerator: 64, 299 | denominator: 1, 300 | } 301 | } 302 | 303 | fn now() -> Self::Instant { 304 | Instant::now() 305 | } 306 | 307 | unsafe fn reset() { 308 | let timer = &*target::TIMER1::ptr(); 309 | 310 | // Clear the counter value 311 | timer.tasks_clear.write(|w| w.bits(1)); 312 | } 313 | 314 | fn zero() -> Self::Instant { 315 | Instant { inner: 0 } 316 | } 317 | } 318 | --------------------------------------------------------------------------------