├── .gitignore ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── ch0_start ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ └── main.rs ├── ch1_setup ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ └── main.rs ├── ch2_timekeeping ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ ├── main.rs │ └── time.rs ├── ch3_state_machines ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ ├── button.rs │ ├── channel.rs │ ├── led.rs │ ├── main.rs │ └── time.rs ├── ch4_interrupts ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ ├── button.rs │ ├── channel.rs │ ├── led.rs │ ├── main.rs │ └── time.rs ├── ch5_futures ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ ├── button.rs │ ├── channel.rs │ ├── executor.rs │ ├── future.rs │ ├── gpiote.rs │ ├── led.rs │ ├── main.rs │ └── time.rs ├── ch6_async_await ├── .cargo │ └── config.toml ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src │ ├── button.rs │ ├── channel.rs │ ├── executor.rs │ ├── gpiote.rs │ ├── led.rs │ ├── main.rs │ └── time.rs └── ch7_embassy ├── .cargo └── config.toml ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Embed.toml ├── memory.x └── src ├── button.rs ├── led.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chris Suozzo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zero to Async 2 | 3 | This is the companion repository for the [YouTube video](https://youtu.be/wni5h5vIPhU), where we walk through 4 | the creation of a basic `async` runtime in embedded Rust from the ground up. 5 | The code for the end of each chapter is in its respective directory. 6 | 7 | Is it perfect? No! But some parts of it might be helpful to those (ok, me) 8 | trying to learn about `async/await` or using Rust in embedded systems. 9 | 10 | My apologies to Ferris for the use of `unsafe` in a few places: some were 11 | avoidable (PAC access via raw pointer), while others were not (`no-std` 12 | `Waker`/`RawWaker` stuff). 13 | 14 | ## Prerequisites 15 | 16 | A burning desire to learn new things? And watching the two videos leading up to 17 | this one, of course, which cover: 18 | - [How to get setup for embedded development in Rust](https://youtu.be/TOAynddiu5M) 19 | - [Blinking an LED & the embedded Rust ecosystem](https://youtu.be/A9wvA_S6m7Y) 20 | 21 | Also: reading the [Rust book](https://doc.rust-lang.org/book/) is always a good idea 22 | 23 | ## Further Research 24 | 25 | Can't get enough `async` embedded Rust? Then I'd encourage you to check out: 26 | - [`embassy`](https://embassy.dev/) 27 | - [Dario Nieuwenhuis' 2024 RustNL talk introducing `embassy`](https://youtu.be/H7NtzyP9q8E) 28 | - [`lilos`](https://github.com/cbiffle/lilos/) 29 | - [Cliff Biffle's 2023 OSFC talk introducing `lilos`](https://www.osfc.io/2023/talks/turn-your-code-inside-out-programming-and-debugging-bare-metal-with-async-rust/) 30 | - [Cliff's blog](https://cliffle.com/blog) 31 | 32 | If you're looking for a similar bottom-up exploration of `async` Rust but this 33 | time using the standard library, check out the book ["Asynchronous Programming 34 | in Rust" by Carl Fredrik Samson.](https://www.packtpub.com/en-us/product/asynchronous-programming-in-rust-9781805128137) 35 | 36 | Any discussion of `async` Rust would be incomplete without mentioning 37 | [without.boats blog](https://without.boats/blog/), which is full of great 38 | articles about the history of `async/await` within the Rust project, `async` 39 | topics like Pinning, and possible future directions for `async`. 40 | -------------------------------------------------------------------------------- /ch0_start/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch0_start/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch0_start/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "half" 146 | version = "2.4.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 149 | dependencies = [ 150 | "cfg-if", 151 | "crunchy", 152 | ] 153 | 154 | [[package]] 155 | name = "hash32" 156 | version = "0.3.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 159 | dependencies = [ 160 | "byteorder", 161 | ] 162 | 163 | [[package]] 164 | name = "heapless" 165 | version = "0.8.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 168 | dependencies = [ 169 | "hash32", 170 | "stable_deref_trait", 171 | ] 172 | 173 | [[package]] 174 | name = "microbit-common" 175 | version = "0.15.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 178 | dependencies = [ 179 | "embedded-hal 1.0.0", 180 | "nrf52833-hal", 181 | "tiny-led-matrix", 182 | ] 183 | 184 | [[package]] 185 | name = "microbit-v2" 186 | version = "0.15.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 189 | dependencies = [ 190 | "microbit-common", 191 | ] 192 | 193 | [[package]] 194 | name = "nb" 195 | version = "0.1.3" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 198 | dependencies = [ 199 | "nb 1.1.0", 200 | ] 201 | 202 | [[package]] 203 | name = "nb" 204 | version = "1.1.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 207 | 208 | [[package]] 209 | name = "nrf-hal-common" 210 | version = "0.18.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 213 | dependencies = [ 214 | "cast", 215 | "cfg-if", 216 | "cortex-m", 217 | "embedded-dma", 218 | "embedded-hal 1.0.0", 219 | "embedded-io", 220 | "embedded-storage", 221 | "fixed", 222 | "nb 1.1.0", 223 | "nrf-usbd", 224 | "nrf52833-pac", 225 | "rand_core", 226 | "void", 227 | ] 228 | 229 | [[package]] 230 | name = "nrf-usbd" 231 | version = "0.3.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 234 | dependencies = [ 235 | "cortex-m", 236 | "critical-section", 237 | "usb-device", 238 | "vcell", 239 | ] 240 | 241 | [[package]] 242 | name = "nrf52833-hal" 243 | version = "0.18.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 246 | dependencies = [ 247 | "nrf-hal-common", 248 | "nrf52833-pac", 249 | ] 250 | 251 | [[package]] 252 | name = "nrf52833-pac" 253 | version = "0.12.2" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 256 | dependencies = [ 257 | "cortex-m", 258 | "cortex-m-rt", 259 | "vcell", 260 | ] 261 | 262 | [[package]] 263 | name = "panic-rtt-target" 264 | version = "0.1.3" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 267 | dependencies = [ 268 | "critical-section", 269 | "rtt-target", 270 | ] 271 | 272 | [[package]] 273 | name = "portable-atomic" 274 | version = "1.7.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 277 | 278 | [[package]] 279 | name = "proc-macro2" 280 | version = "1.0.86" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 283 | dependencies = [ 284 | "unicode-ident", 285 | ] 286 | 287 | [[package]] 288 | name = "quote" 289 | version = "1.0.36" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 292 | dependencies = [ 293 | "proc-macro2", 294 | ] 295 | 296 | [[package]] 297 | name = "rand_core" 298 | version = "0.6.4" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 301 | 302 | [[package]] 303 | name = "rtt-target" 304 | version = "0.5.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 307 | dependencies = [ 308 | "critical-section", 309 | "ufmt-write", 310 | ] 311 | 312 | [[package]] 313 | name = "rustc_version" 314 | version = "0.2.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 317 | dependencies = [ 318 | "semver", 319 | ] 320 | 321 | [[package]] 322 | name = "semver" 323 | version = "0.9.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 326 | dependencies = [ 327 | "semver-parser", 328 | ] 329 | 330 | [[package]] 331 | name = "semver-parser" 332 | version = "0.7.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 335 | 336 | [[package]] 337 | name = "stable_deref_trait" 338 | version = "1.2.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 341 | 342 | [[package]] 343 | name = "syn" 344 | version = "1.0.109" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 347 | dependencies = [ 348 | "proc-macro2", 349 | "quote", 350 | "unicode-ident", 351 | ] 352 | 353 | [[package]] 354 | name = "tiny-led-matrix" 355 | version = "1.0.2" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 358 | 359 | [[package]] 360 | name = "typenum" 361 | version = "1.17.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 364 | 365 | [[package]] 366 | name = "ufmt-write" 367 | version = "0.1.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 370 | 371 | [[package]] 372 | name = "unicode-ident" 373 | version = "1.0.12" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 376 | 377 | [[package]] 378 | name = "usb-device" 379 | version = "0.3.2" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 382 | dependencies = [ 383 | "heapless", 384 | "portable-atomic", 385 | ] 386 | 387 | [[package]] 388 | name = "vcell" 389 | version = "0.1.3" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 392 | 393 | [[package]] 394 | name = "void" 395 | version = "1.0.2" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 398 | 399 | [[package]] 400 | name = "volatile-register" 401 | version = "0.2.2" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 404 | dependencies = [ 405 | "vcell", 406 | ] 407 | 408 | [[package]] 409 | name = "zero-to-async" 410 | version = "0.1.0" 411 | dependencies = [ 412 | "cortex-m", 413 | "cortex-m-rt", 414 | "embedded-hal 1.0.0", 415 | "microbit-v2", 416 | "panic-rtt-target", 417 | "rtt-target", 418 | ] 419 | -------------------------------------------------------------------------------- /ch0_start/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | embedded-hal = "1.0.0" 10 | microbit-v2 = "0.15.0" 11 | panic-rtt-target = "0.1.3" 12 | rtt-target = "0.5.0" 13 | -------------------------------------------------------------------------------- /ch0_start/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch0_start/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch0_start/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m_rt::entry; 5 | use embedded_hal::{ 6 | delay::DelayNs, 7 | digital::{OutputPin, StatefulOutputPin}, 8 | }; 9 | use microbit::{hal::Timer, Board}; 10 | use panic_rtt_target as _; 11 | use rtt_target::rtt_init_print; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | rtt_init_print!(); 16 | let mut board = Board::take().unwrap(); 17 | let mut timer = Timer::new(board.TIMER0); 18 | let _ = board.display_pins.col1.set_low(); 19 | let mut row1 = board.display_pins.row1; 20 | 21 | loop { 22 | row1.toggle().ok(); 23 | timer.delay_ms(500); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ch1_setup/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch1_setup/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch1_setup/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "half" 146 | version = "2.4.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 149 | dependencies = [ 150 | "cfg-if", 151 | "crunchy", 152 | ] 153 | 154 | [[package]] 155 | name = "hash32" 156 | version = "0.3.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 159 | dependencies = [ 160 | "byteorder", 161 | ] 162 | 163 | [[package]] 164 | name = "heapless" 165 | version = "0.8.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 168 | dependencies = [ 169 | "hash32", 170 | "stable_deref_trait", 171 | ] 172 | 173 | [[package]] 174 | name = "microbit-common" 175 | version = "0.15.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 178 | dependencies = [ 179 | "embedded-hal 1.0.0", 180 | "nrf52833-hal", 181 | "tiny-led-matrix", 182 | ] 183 | 184 | [[package]] 185 | name = "microbit-v2" 186 | version = "0.15.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 189 | dependencies = [ 190 | "microbit-common", 191 | ] 192 | 193 | [[package]] 194 | name = "nb" 195 | version = "0.1.3" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 198 | dependencies = [ 199 | "nb 1.1.0", 200 | ] 201 | 202 | [[package]] 203 | name = "nb" 204 | version = "1.1.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 207 | 208 | [[package]] 209 | name = "nrf-hal-common" 210 | version = "0.18.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 213 | dependencies = [ 214 | "cast", 215 | "cfg-if", 216 | "cortex-m", 217 | "embedded-dma", 218 | "embedded-hal 1.0.0", 219 | "embedded-io", 220 | "embedded-storage", 221 | "fixed", 222 | "nb 1.1.0", 223 | "nrf-usbd", 224 | "nrf52833-pac", 225 | "rand_core", 226 | "void", 227 | ] 228 | 229 | [[package]] 230 | name = "nrf-usbd" 231 | version = "0.3.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 234 | dependencies = [ 235 | "cortex-m", 236 | "critical-section", 237 | "usb-device", 238 | "vcell", 239 | ] 240 | 241 | [[package]] 242 | name = "nrf52833-hal" 243 | version = "0.18.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 246 | dependencies = [ 247 | "nrf-hal-common", 248 | "nrf52833-pac", 249 | ] 250 | 251 | [[package]] 252 | name = "nrf52833-pac" 253 | version = "0.12.2" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 256 | dependencies = [ 257 | "cortex-m", 258 | "cortex-m-rt", 259 | "vcell", 260 | ] 261 | 262 | [[package]] 263 | name = "panic-rtt-target" 264 | version = "0.1.3" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 267 | dependencies = [ 268 | "critical-section", 269 | "rtt-target", 270 | ] 271 | 272 | [[package]] 273 | name = "portable-atomic" 274 | version = "1.7.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 277 | 278 | [[package]] 279 | name = "proc-macro2" 280 | version = "1.0.86" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 283 | dependencies = [ 284 | "unicode-ident", 285 | ] 286 | 287 | [[package]] 288 | name = "quote" 289 | version = "1.0.36" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 292 | dependencies = [ 293 | "proc-macro2", 294 | ] 295 | 296 | [[package]] 297 | name = "rand_core" 298 | version = "0.6.4" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 301 | 302 | [[package]] 303 | name = "rtt-target" 304 | version = "0.5.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 307 | dependencies = [ 308 | "critical-section", 309 | "ufmt-write", 310 | ] 311 | 312 | [[package]] 313 | name = "rustc_version" 314 | version = "0.2.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 317 | dependencies = [ 318 | "semver", 319 | ] 320 | 321 | [[package]] 322 | name = "semver" 323 | version = "0.9.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 326 | dependencies = [ 327 | "semver-parser", 328 | ] 329 | 330 | [[package]] 331 | name = "semver-parser" 332 | version = "0.7.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 335 | 336 | [[package]] 337 | name = "stable_deref_trait" 338 | version = "1.2.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 341 | 342 | [[package]] 343 | name = "syn" 344 | version = "1.0.109" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 347 | dependencies = [ 348 | "proc-macro2", 349 | "quote", 350 | "unicode-ident", 351 | ] 352 | 353 | [[package]] 354 | name = "tiny-led-matrix" 355 | version = "1.0.2" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 358 | 359 | [[package]] 360 | name = "typenum" 361 | version = "1.17.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 364 | 365 | [[package]] 366 | name = "ufmt-write" 367 | version = "0.1.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 370 | 371 | [[package]] 372 | name = "unicode-ident" 373 | version = "1.0.12" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 376 | 377 | [[package]] 378 | name = "usb-device" 379 | version = "0.3.2" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 382 | dependencies = [ 383 | "heapless", 384 | "portable-atomic", 385 | ] 386 | 387 | [[package]] 388 | name = "vcell" 389 | version = "0.1.3" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 392 | 393 | [[package]] 394 | name = "void" 395 | version = "1.0.2" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 398 | 399 | [[package]] 400 | name = "volatile-register" 401 | version = "0.2.2" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 404 | dependencies = [ 405 | "vcell", 406 | ] 407 | 408 | [[package]] 409 | name = "zero-to-async" 410 | version = "0.1.0" 411 | dependencies = [ 412 | "cortex-m", 413 | "cortex-m-rt", 414 | "embedded-hal 1.0.0", 415 | "microbit-v2", 416 | "panic-rtt-target", 417 | "rtt-target", 418 | ] 419 | -------------------------------------------------------------------------------- /ch1_setup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | embedded-hal = "1.0.0" 10 | microbit-v2 = "0.15.0" 11 | panic-rtt-target = "0.1.3" 12 | rtt-target = "0.5.0" 13 | -------------------------------------------------------------------------------- /ch1_setup/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch1_setup/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch1_setup/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use cortex_m_rt::entry; 5 | use embedded_hal::{ 6 | delay::DelayNs, 7 | digital::{InputPin, OutputPin, StatefulOutputPin}, 8 | }; 9 | use microbit::{hal::Timer, Board}; 10 | use panic_rtt_target as _; 11 | use rtt_target::rtt_init_print; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | rtt_init_print!(); 16 | let board = Board::take().unwrap(); 17 | let mut timer = Timer::new(board.TIMER0); 18 | let (mut col, mut row) = board.display_pins.degrade(); 19 | row[0].set_high().ok(); 20 | let mut button_l = board.buttons.button_a.degrade(); 21 | let mut button_r = board.buttons.button_b.degrade(); 22 | 23 | let active_col: usize = 0; 24 | loop { 25 | col[active_col].toggle().ok(); 26 | // blocking here: 27 | timer.delay_ms(500); 28 | // will prevent timely detection & response to these: 29 | if button_l.is_low().unwrap() { 30 | //.. 31 | } 32 | if button_r.is_low().unwrap() { 33 | //.. 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ch2_timekeeping/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch2_timekeeping/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch2_timekeeping/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "fugit" 146 | version = "0.3.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 149 | dependencies = [ 150 | "gcd", 151 | ] 152 | 153 | [[package]] 154 | name = "gcd" 155 | version = "2.3.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 158 | 159 | [[package]] 160 | name = "half" 161 | version = "2.4.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 164 | dependencies = [ 165 | "cfg-if", 166 | "crunchy", 167 | ] 168 | 169 | [[package]] 170 | name = "hash32" 171 | version = "0.3.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 174 | dependencies = [ 175 | "byteorder", 176 | ] 177 | 178 | [[package]] 179 | name = "heapless" 180 | version = "0.8.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 183 | dependencies = [ 184 | "hash32", 185 | "stable_deref_trait", 186 | ] 187 | 188 | [[package]] 189 | name = "microbit-common" 190 | version = "0.15.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 193 | dependencies = [ 194 | "embedded-hal 1.0.0", 195 | "nrf52833-hal", 196 | "tiny-led-matrix", 197 | ] 198 | 199 | [[package]] 200 | name = "microbit-v2" 201 | version = "0.15.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 204 | dependencies = [ 205 | "microbit-common", 206 | ] 207 | 208 | [[package]] 209 | name = "nb" 210 | version = "0.1.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 213 | dependencies = [ 214 | "nb 1.1.0", 215 | ] 216 | 217 | [[package]] 218 | name = "nb" 219 | version = "1.1.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 222 | 223 | [[package]] 224 | name = "nrf-hal-common" 225 | version = "0.18.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 228 | dependencies = [ 229 | "cast", 230 | "cfg-if", 231 | "cortex-m", 232 | "embedded-dma", 233 | "embedded-hal 1.0.0", 234 | "embedded-io", 235 | "embedded-storage", 236 | "fixed", 237 | "nb 1.1.0", 238 | "nrf-usbd", 239 | "nrf52833-pac", 240 | "rand_core", 241 | "void", 242 | ] 243 | 244 | [[package]] 245 | name = "nrf-usbd" 246 | version = "0.3.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 249 | dependencies = [ 250 | "cortex-m", 251 | "critical-section", 252 | "usb-device", 253 | "vcell", 254 | ] 255 | 256 | [[package]] 257 | name = "nrf52833-hal" 258 | version = "0.18.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 261 | dependencies = [ 262 | "nrf-hal-common", 263 | "nrf52833-pac", 264 | ] 265 | 266 | [[package]] 267 | name = "nrf52833-pac" 268 | version = "0.12.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 271 | dependencies = [ 272 | "cortex-m", 273 | "cortex-m-rt", 274 | "vcell", 275 | ] 276 | 277 | [[package]] 278 | name = "panic-rtt-target" 279 | version = "0.1.3" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 282 | dependencies = [ 283 | "critical-section", 284 | "rtt-target", 285 | ] 286 | 287 | [[package]] 288 | name = "portable-atomic" 289 | version = "1.7.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 292 | 293 | [[package]] 294 | name = "proc-macro2" 295 | version = "1.0.86" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 298 | dependencies = [ 299 | "unicode-ident", 300 | ] 301 | 302 | [[package]] 303 | name = "quote" 304 | version = "1.0.36" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 307 | dependencies = [ 308 | "proc-macro2", 309 | ] 310 | 311 | [[package]] 312 | name = "rand_core" 313 | version = "0.6.4" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 316 | 317 | [[package]] 318 | name = "rtt-target" 319 | version = "0.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 322 | dependencies = [ 323 | "critical-section", 324 | "ufmt-write", 325 | ] 326 | 327 | [[package]] 328 | name = "rustc_version" 329 | version = "0.2.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 332 | dependencies = [ 333 | "semver", 334 | ] 335 | 336 | [[package]] 337 | name = "semver" 338 | version = "0.9.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 341 | dependencies = [ 342 | "semver-parser", 343 | ] 344 | 345 | [[package]] 346 | name = "semver-parser" 347 | version = "0.7.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 350 | 351 | [[package]] 352 | name = "stable_deref_trait" 353 | version = "1.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 356 | 357 | [[package]] 358 | name = "syn" 359 | version = "1.0.109" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 362 | dependencies = [ 363 | "proc-macro2", 364 | "quote", 365 | "unicode-ident", 366 | ] 367 | 368 | [[package]] 369 | name = "tiny-led-matrix" 370 | version = "1.0.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 373 | 374 | [[package]] 375 | name = "typenum" 376 | version = "1.17.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 379 | 380 | [[package]] 381 | name = "ufmt-write" 382 | version = "0.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 385 | 386 | [[package]] 387 | name = "unicode-ident" 388 | version = "1.0.12" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 391 | 392 | [[package]] 393 | name = "usb-device" 394 | version = "0.3.2" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 397 | dependencies = [ 398 | "heapless", 399 | "portable-atomic", 400 | ] 401 | 402 | [[package]] 403 | name = "vcell" 404 | version = "0.1.3" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 407 | 408 | [[package]] 409 | name = "void" 410 | version = "1.0.2" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 413 | 414 | [[package]] 415 | name = "volatile-register" 416 | version = "0.2.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 419 | dependencies = [ 420 | "vcell", 421 | ] 422 | 423 | [[package]] 424 | name = "zero-to-async" 425 | version = "0.1.0" 426 | dependencies = [ 427 | "cortex-m", 428 | "cortex-m-rt", 429 | "embedded-hal 1.0.0", 430 | "fugit", 431 | "microbit-v2", 432 | "panic-rtt-target", 433 | "rtt-target", 434 | ] 435 | -------------------------------------------------------------------------------- /ch2_timekeeping/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | embedded-hal = "1.0.0" 10 | fugit = "0.3.7" 11 | microbit-v2 = "0.15.0" 12 | panic-rtt-target = "0.1.3" 13 | rtt-target = "0.5.0" 14 | -------------------------------------------------------------------------------- /ch2_timekeeping/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch2_timekeeping/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch2_timekeeping/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod time; 5 | 6 | use cortex_m_rt::entry; 7 | use embedded_hal::{ 8 | delay::DelayNs, 9 | digital::{InputPin, OutputPin, StatefulOutputPin}, 10 | }; 11 | use microbit::{hal::Timer, Board}; 12 | use panic_rtt_target as _; 13 | use rtt_target::rtt_init_print; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | rtt_init_print!(); 18 | let board = Board::take().unwrap(); 19 | let mut timer = Timer::new(board.TIMER0); 20 | let (mut col, mut row) = board.display_pins.degrade(); 21 | row[0].set_high().ok(); 22 | let mut button_l = board.buttons.button_a.degrade(); 23 | let mut button_r = board.buttons.button_b.degrade(); 24 | 25 | let active_col: usize = 0; 26 | loop { 27 | col[active_col].toggle().ok(); 28 | // blocking here: 29 | timer.delay_ms(500); 30 | // will prevent timely detection & response to these: 31 | if button_l.is_low().unwrap() { 32 | //.. 33 | } 34 | if button_r.is_low().unwrap() { 35 | //.. 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ch2_timekeeping/src/time.rs: -------------------------------------------------------------------------------- 1 | use fugit::{Duration, Instant}; 2 | use microbit::{hal::Rtc, pac::RTC0}; 3 | 4 | type TickInstant = Instant; 5 | type TickDuration = Duration; 6 | 7 | pub struct Timer<'a> { 8 | end_time: TickInstant, 9 | ticker: &'a Ticker, 10 | } 11 | 12 | impl<'a> Timer<'a> { 13 | pub fn new(duration: TickDuration, ticker: &'a Ticker) -> Self { 14 | Self { 15 | end_time: ticker.now() + duration, 16 | ticker, 17 | } 18 | } 19 | 20 | pub fn is_ready(&self) -> bool { 21 | self.ticker.now() >= self.end_time 22 | } 23 | } 24 | 25 | /// Keeps track of time for the system using RTC0, which ticks away at a rate 26 | /// of 32,768/sec using a low-power oscillator that runs even when the core is 27 | /// powered down. 28 | /// 29 | /// RTC0's counter is only 24-bits wide, which means there will be an overflow 30 | /// every ~8min, which we do not account for: this will be fixed in chapter 4. 31 | pub struct Ticker { 32 | rtc: Rtc, 33 | } 34 | 35 | impl Ticker { 36 | /// Create on startup to get RTC0 going. 37 | pub fn new(rtc0: RTC0) -> Self { 38 | let rtc = Rtc::new(rtc0, 0).unwrap(); 39 | rtc.enable_counter(); 40 | Self { rtc } 41 | } 42 | 43 | pub fn now(&self) -> TickInstant { 44 | TickInstant::from_ticks(self.rtc.get_counter() as u64) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ch3_state_machines/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch3_state_machines/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch3_state_machines/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "fugit" 146 | version = "0.3.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 149 | dependencies = [ 150 | "gcd", 151 | ] 152 | 153 | [[package]] 154 | name = "gcd" 155 | version = "2.3.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 158 | 159 | [[package]] 160 | name = "half" 161 | version = "2.4.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 164 | dependencies = [ 165 | "cfg-if", 166 | "crunchy", 167 | ] 168 | 169 | [[package]] 170 | name = "hash32" 171 | version = "0.3.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 174 | dependencies = [ 175 | "byteorder", 176 | ] 177 | 178 | [[package]] 179 | name = "heapless" 180 | version = "0.8.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 183 | dependencies = [ 184 | "hash32", 185 | "stable_deref_trait", 186 | ] 187 | 188 | [[package]] 189 | name = "microbit-common" 190 | version = "0.15.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 193 | dependencies = [ 194 | "embedded-hal 1.0.0", 195 | "nrf52833-hal", 196 | "tiny-led-matrix", 197 | ] 198 | 199 | [[package]] 200 | name = "microbit-v2" 201 | version = "0.15.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 204 | dependencies = [ 205 | "microbit-common", 206 | ] 207 | 208 | [[package]] 209 | name = "nb" 210 | version = "0.1.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 213 | dependencies = [ 214 | "nb 1.1.0", 215 | ] 216 | 217 | [[package]] 218 | name = "nb" 219 | version = "1.1.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 222 | 223 | [[package]] 224 | name = "nrf-hal-common" 225 | version = "0.18.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 228 | dependencies = [ 229 | "cast", 230 | "cfg-if", 231 | "cortex-m", 232 | "embedded-dma", 233 | "embedded-hal 1.0.0", 234 | "embedded-io", 235 | "embedded-storage", 236 | "fixed", 237 | "nb 1.1.0", 238 | "nrf-usbd", 239 | "nrf52833-pac", 240 | "rand_core", 241 | "void", 242 | ] 243 | 244 | [[package]] 245 | name = "nrf-usbd" 246 | version = "0.3.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 249 | dependencies = [ 250 | "cortex-m", 251 | "critical-section", 252 | "usb-device", 253 | "vcell", 254 | ] 255 | 256 | [[package]] 257 | name = "nrf52833-hal" 258 | version = "0.18.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 261 | dependencies = [ 262 | "nrf-hal-common", 263 | "nrf52833-pac", 264 | ] 265 | 266 | [[package]] 267 | name = "nrf52833-pac" 268 | version = "0.12.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 271 | dependencies = [ 272 | "cortex-m", 273 | "cortex-m-rt", 274 | "vcell", 275 | ] 276 | 277 | [[package]] 278 | name = "panic-rtt-target" 279 | version = "0.1.3" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 282 | dependencies = [ 283 | "critical-section", 284 | "rtt-target", 285 | ] 286 | 287 | [[package]] 288 | name = "portable-atomic" 289 | version = "1.7.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 292 | 293 | [[package]] 294 | name = "proc-macro2" 295 | version = "1.0.86" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 298 | dependencies = [ 299 | "unicode-ident", 300 | ] 301 | 302 | [[package]] 303 | name = "quote" 304 | version = "1.0.36" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 307 | dependencies = [ 308 | "proc-macro2", 309 | ] 310 | 311 | [[package]] 312 | name = "rand_core" 313 | version = "0.6.4" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 316 | 317 | [[package]] 318 | name = "rtt-target" 319 | version = "0.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 322 | dependencies = [ 323 | "critical-section", 324 | "ufmt-write", 325 | ] 326 | 327 | [[package]] 328 | name = "rustc_version" 329 | version = "0.2.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 332 | dependencies = [ 333 | "semver", 334 | ] 335 | 336 | [[package]] 337 | name = "semver" 338 | version = "0.9.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 341 | dependencies = [ 342 | "semver-parser", 343 | ] 344 | 345 | [[package]] 346 | name = "semver-parser" 347 | version = "0.7.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 350 | 351 | [[package]] 352 | name = "stable_deref_trait" 353 | version = "1.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 356 | 357 | [[package]] 358 | name = "syn" 359 | version = "1.0.109" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 362 | dependencies = [ 363 | "proc-macro2", 364 | "quote", 365 | "unicode-ident", 366 | ] 367 | 368 | [[package]] 369 | name = "tiny-led-matrix" 370 | version = "1.0.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 373 | 374 | [[package]] 375 | name = "typenum" 376 | version = "1.17.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 379 | 380 | [[package]] 381 | name = "ufmt-write" 382 | version = "0.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 385 | 386 | [[package]] 387 | name = "unicode-ident" 388 | version = "1.0.12" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 391 | 392 | [[package]] 393 | name = "usb-device" 394 | version = "0.3.2" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 397 | dependencies = [ 398 | "heapless", 399 | "portable-atomic", 400 | ] 401 | 402 | [[package]] 403 | name = "vcell" 404 | version = "0.1.3" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 407 | 408 | [[package]] 409 | name = "void" 410 | version = "1.0.2" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 413 | 414 | [[package]] 415 | name = "volatile-register" 416 | version = "0.2.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 419 | dependencies = [ 420 | "vcell", 421 | ] 422 | 423 | [[package]] 424 | name = "zero-to-async" 425 | version = "0.1.0" 426 | dependencies = [ 427 | "cortex-m", 428 | "cortex-m-rt", 429 | "embedded-hal 1.0.0", 430 | "fugit", 431 | "microbit-v2", 432 | "panic-rtt-target", 433 | "rtt-target", 434 | ] 435 | -------------------------------------------------------------------------------- /ch3_state_machines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | embedded-hal = "1.0.0" 10 | fugit = "0.3.7" 11 | microbit-v2 = "0.15.0" 12 | panic-rtt-target = "0.1.3" 13 | rtt-target = "0.5.0" 14 | -------------------------------------------------------------------------------- /ch3_state_machines/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch3_state_machines/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch3_state_machines/src/button.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::InputPin; 2 | use fugit::ExtU64; 3 | use microbit::hal::gpio::{Floating, Input, Pin}; 4 | 5 | use crate::{ 6 | channel::Sender, 7 | time::{Ticker, Timer}, 8 | }; 9 | 10 | #[derive(Clone, Copy)] 11 | pub enum ButtonDirection { 12 | Left, 13 | Right, 14 | } 15 | 16 | enum ButtonState<'a> { 17 | WaitForPress, 18 | Debounce(Timer<'a>), 19 | } 20 | 21 | pub struct ButtonTask<'a> { 22 | pin: Pin>, 23 | ticker: &'a Ticker, 24 | direction: ButtonDirection, 25 | state: ButtonState<'a>, 26 | sender: Sender<'a, ButtonDirection>, 27 | } 28 | 29 | impl<'a> ButtonTask<'a> { 30 | pub fn new( 31 | pin: Pin>, 32 | ticker: &'a Ticker, 33 | direction: ButtonDirection, 34 | sender: Sender<'a, ButtonDirection>, 35 | ) -> Self { 36 | Self { 37 | pin, 38 | ticker, 39 | direction, 40 | state: ButtonState::WaitForPress, 41 | sender, 42 | } 43 | } 44 | 45 | pub fn poll(&mut self) { 46 | match self.state { 47 | ButtonState::WaitForPress => { 48 | if self.pin.is_low().unwrap() { 49 | self.sender.send(self.direction); 50 | self.state = ButtonState::Debounce(Timer::new(100.millis(), &self.ticker)); 51 | } 52 | } 53 | ButtonState::Debounce(ref timer) => { 54 | if timer.is_ready() && self.pin.is_high().unwrap() { 55 | self.state = ButtonState::WaitForPress; 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ch3_state_machines/src/channel.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | 3 | pub struct Channel { 4 | item: Cell>, 5 | } 6 | 7 | impl Channel { 8 | pub fn new() -> Self { 9 | Self { 10 | item: Cell::new(None), 11 | } 12 | } 13 | 14 | pub fn get_sender(&self) -> Sender { 15 | Sender { channel: &self } 16 | } 17 | 18 | pub fn get_receiver(&self) -> Receiver { 19 | Receiver { channel: &self } 20 | } 21 | 22 | fn send(&self, item: T) { 23 | self.item.replace(Some(item)); 24 | } 25 | 26 | fn receive(&self) -> Option { 27 | self.item.take() 28 | } 29 | } 30 | 31 | pub struct Sender<'a, T> { 32 | channel: &'a Channel, 33 | } 34 | 35 | impl Sender<'_, T> { 36 | pub fn send(&self, item: T) { 37 | self.channel.send(item); 38 | } 39 | } 40 | 41 | pub struct Receiver<'a, T> { 42 | channel: &'a Channel, 43 | } 44 | 45 | impl Receiver<'_, T> { 46 | pub fn receive(&self) -> Option { 47 | self.channel.receive() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch3_state_machines/src/led.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::{OutputPin, StatefulOutputPin}; 2 | use fugit::ExtU64; 3 | use microbit::{ 4 | gpio::NUM_COLS, 5 | hal::gpio::{Output, Pin, PushPull}, 6 | }; 7 | use rtt_target::rprintln; 8 | 9 | use crate::{ 10 | button::ButtonDirection, 11 | channel::Receiver, 12 | time::{Ticker, Timer}, 13 | }; 14 | 15 | enum LedState<'a> { 16 | Toggle, 17 | Wait(Timer<'a>), 18 | } 19 | 20 | pub struct LedTask<'a> { 21 | col: [Pin>; NUM_COLS], 22 | active_col: usize, 23 | ticker: &'a Ticker, 24 | state: LedState<'a>, 25 | receiver: Receiver<'a, ButtonDirection>, 26 | } 27 | 28 | impl<'a> LedTask<'a> { 29 | pub fn new( 30 | col: [Pin>; NUM_COLS], 31 | ticker: &'a Ticker, 32 | receiver: Receiver<'a, ButtonDirection>, 33 | ) -> Self { 34 | Self { 35 | col, 36 | active_col: 0, 37 | ticker, 38 | state: LedState::Toggle, 39 | receiver, 40 | } 41 | } 42 | 43 | fn shift(&mut self, direction: ButtonDirection) { 44 | rprintln!("Button press detected.."); 45 | // switch off current/old LED 46 | self.col[self.active_col].set_high().ok(); 47 | self.active_col = match direction { 48 | ButtonDirection::Left => match self.active_col { 49 | 0 => NUM_COLS - 1, 50 | _ => self.active_col - 1, 51 | } 52 | ButtonDirection::Right => (self.active_col + 1) % NUM_COLS, 53 | }; 54 | // switch off new LED: moving to Toggle will then switch it on 55 | self.col[self.active_col].set_high().ok(); 56 | } 57 | 58 | pub fn poll(&mut self) { 59 | match self.state { 60 | LedState::Toggle => { 61 | rprintln!("Blinking LED {}", self.active_col); 62 | self.col[self.active_col].toggle().ok(); 63 | self.state = LedState::Wait(Timer::new(500.millis(), &self.ticker)); 64 | } 65 | LedState::Wait(ref timer) => { 66 | if timer.is_ready() { 67 | self.state = LedState::Toggle; 68 | } 69 | if let Some(direction) = self.receiver.receive() { 70 | self.shift(direction); 71 | self.state = LedState::Toggle; 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ch3_state_machines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod button; 5 | mod channel; 6 | mod led; 7 | mod time; 8 | 9 | use button::{ButtonDirection, ButtonTask}; 10 | use channel::Channel; 11 | use cortex_m_rt::entry; 12 | use embedded_hal::digital::OutputPin; 13 | use led::LedTask; 14 | use microbit::Board; 15 | use panic_rtt_target as _; 16 | use rtt_target::rtt_init_print; 17 | use time::Ticker; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | rtt_init_print!(); 22 | let board = Board::take().unwrap(); 23 | let ticker = Ticker::new(board.RTC0); 24 | let (col, mut row) = board.display_pins.degrade(); 25 | row[0].set_high().ok(); 26 | let button_l = board.buttons.button_a.degrade(); 27 | let button_r = board.buttons.button_b.degrade(); 28 | 29 | let channel: Channel = Channel::new(); 30 | let mut led_task = LedTask::new(col, &ticker, channel.get_receiver()); 31 | let mut button_l_task = ButtonTask::new( 32 | button_l, 33 | &ticker, 34 | ButtonDirection::Left, 35 | channel.get_sender(), 36 | ); 37 | let mut button_r_task = ButtonTask::new( 38 | button_r, 39 | &ticker, 40 | ButtonDirection::Right, 41 | channel.get_sender(), 42 | ); 43 | 44 | loop { 45 | led_task.poll(); 46 | button_l_task.poll(); 47 | button_r_task.poll(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch3_state_machines/src/time.rs: -------------------------------------------------------------------------------- 1 | use fugit::{Duration, Instant}; 2 | use microbit::{hal::Rtc, pac::RTC0}; 3 | 4 | type TickInstant = Instant; 5 | type TickDuration = Duration; 6 | 7 | pub struct Timer<'a> { 8 | end_time: TickInstant, 9 | ticker: &'a Ticker, 10 | } 11 | 12 | impl<'a> Timer<'a> { 13 | pub fn new(duration: TickDuration, ticker: &'a Ticker) -> Self { 14 | Self { 15 | end_time: ticker.now() + duration, 16 | ticker, 17 | } 18 | } 19 | 20 | pub fn is_ready(&self) -> bool { 21 | self.ticker.now() >= self.end_time 22 | } 23 | } 24 | 25 | /// Keeps track of time for the system using RTC0, which ticks away at a rate 26 | /// of 32,768/sec using a low-power oscillator that runs even when the core is 27 | /// powered down. 28 | /// 29 | /// RTC0's counter is only 24-bits wide, which means there will be an overflow 30 | /// every ~8min, which we do not account for: this will be fixed in chapter 4. 31 | pub struct Ticker { 32 | rtc: Rtc, 33 | } 34 | 35 | impl Ticker { 36 | /// Create on startup to get RTC0 going. 37 | pub fn new(rtc0: RTC0) -> Self { 38 | let rtc = Rtc::new(rtc0, 0).unwrap(); 39 | rtc.enable_counter(); 40 | Self { rtc } 41 | } 42 | 43 | pub fn now(&self) -> TickInstant { 44 | TickInstant::from_ticks(self.rtc.get_counter() as u64) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ch4_interrupts/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch4_interrupts/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch4_interrupts/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "fugit" 146 | version = "0.3.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 149 | dependencies = [ 150 | "gcd", 151 | ] 152 | 153 | [[package]] 154 | name = "gcd" 155 | version = "2.3.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 158 | 159 | [[package]] 160 | name = "half" 161 | version = "2.4.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 164 | dependencies = [ 165 | "cfg-if", 166 | "crunchy", 167 | ] 168 | 169 | [[package]] 170 | name = "hash32" 171 | version = "0.3.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 174 | dependencies = [ 175 | "byteorder", 176 | ] 177 | 178 | [[package]] 179 | name = "heapless" 180 | version = "0.8.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 183 | dependencies = [ 184 | "hash32", 185 | "stable_deref_trait", 186 | ] 187 | 188 | [[package]] 189 | name = "microbit-common" 190 | version = "0.15.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 193 | dependencies = [ 194 | "embedded-hal 1.0.0", 195 | "nrf52833-hal", 196 | "tiny-led-matrix", 197 | ] 198 | 199 | [[package]] 200 | name = "microbit-v2" 201 | version = "0.15.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 204 | dependencies = [ 205 | "microbit-common", 206 | ] 207 | 208 | [[package]] 209 | name = "nb" 210 | version = "0.1.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 213 | dependencies = [ 214 | "nb 1.1.0", 215 | ] 216 | 217 | [[package]] 218 | name = "nb" 219 | version = "1.1.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 222 | 223 | [[package]] 224 | name = "nrf-hal-common" 225 | version = "0.18.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 228 | dependencies = [ 229 | "cast", 230 | "cfg-if", 231 | "cortex-m", 232 | "embedded-dma", 233 | "embedded-hal 1.0.0", 234 | "embedded-io", 235 | "embedded-storage", 236 | "fixed", 237 | "nb 1.1.0", 238 | "nrf-usbd", 239 | "nrf52833-pac", 240 | "rand_core", 241 | "void", 242 | ] 243 | 244 | [[package]] 245 | name = "nrf-usbd" 246 | version = "0.3.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 249 | dependencies = [ 250 | "cortex-m", 251 | "critical-section", 252 | "usb-device", 253 | "vcell", 254 | ] 255 | 256 | [[package]] 257 | name = "nrf52833-hal" 258 | version = "0.18.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 261 | dependencies = [ 262 | "nrf-hal-common", 263 | "nrf52833-pac", 264 | ] 265 | 266 | [[package]] 267 | name = "nrf52833-pac" 268 | version = "0.12.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 271 | dependencies = [ 272 | "cortex-m", 273 | "cortex-m-rt", 274 | "vcell", 275 | ] 276 | 277 | [[package]] 278 | name = "panic-rtt-target" 279 | version = "0.1.3" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 282 | dependencies = [ 283 | "critical-section", 284 | "rtt-target", 285 | ] 286 | 287 | [[package]] 288 | name = "portable-atomic" 289 | version = "1.7.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 292 | 293 | [[package]] 294 | name = "proc-macro2" 295 | version = "1.0.86" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 298 | dependencies = [ 299 | "unicode-ident", 300 | ] 301 | 302 | [[package]] 303 | name = "quote" 304 | version = "1.0.36" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 307 | dependencies = [ 308 | "proc-macro2", 309 | ] 310 | 311 | [[package]] 312 | name = "rand_core" 313 | version = "0.6.4" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 316 | 317 | [[package]] 318 | name = "rtt-target" 319 | version = "0.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 322 | dependencies = [ 323 | "critical-section", 324 | "ufmt-write", 325 | ] 326 | 327 | [[package]] 328 | name = "rustc_version" 329 | version = "0.2.3" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 332 | dependencies = [ 333 | "semver", 334 | ] 335 | 336 | [[package]] 337 | name = "semver" 338 | version = "0.9.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 341 | dependencies = [ 342 | "semver-parser", 343 | ] 344 | 345 | [[package]] 346 | name = "semver-parser" 347 | version = "0.7.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 350 | 351 | [[package]] 352 | name = "stable_deref_trait" 353 | version = "1.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 356 | 357 | [[package]] 358 | name = "syn" 359 | version = "1.0.109" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 362 | dependencies = [ 363 | "proc-macro2", 364 | "quote", 365 | "unicode-ident", 366 | ] 367 | 368 | [[package]] 369 | name = "tiny-led-matrix" 370 | version = "1.0.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 373 | 374 | [[package]] 375 | name = "typenum" 376 | version = "1.17.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 379 | 380 | [[package]] 381 | name = "ufmt-write" 382 | version = "0.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 385 | 386 | [[package]] 387 | name = "unicode-ident" 388 | version = "1.0.12" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 391 | 392 | [[package]] 393 | name = "usb-device" 394 | version = "0.3.2" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 397 | dependencies = [ 398 | "heapless", 399 | "portable-atomic", 400 | ] 401 | 402 | [[package]] 403 | name = "vcell" 404 | version = "0.1.3" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 407 | 408 | [[package]] 409 | name = "void" 410 | version = "1.0.2" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 413 | 414 | [[package]] 415 | name = "volatile-register" 416 | version = "0.2.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 419 | dependencies = [ 420 | "vcell", 421 | ] 422 | 423 | [[package]] 424 | name = "zero-to-async" 425 | version = "0.1.0" 426 | dependencies = [ 427 | "cortex-m", 428 | "cortex-m-rt", 429 | "critical-section", 430 | "embedded-hal 1.0.0", 431 | "fugit", 432 | "microbit-v2", 433 | "panic-rtt-target", 434 | "rtt-target", 435 | ] 436 | -------------------------------------------------------------------------------- /ch4_interrupts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | critical-section = "1.1.2" 10 | embedded-hal = "1.0.0" 11 | fugit = "0.3.7" 12 | microbit-v2 = "0.15.0" 13 | panic-rtt-target = "0.1.3" 14 | rtt-target = "0.5.0" 15 | 16 | [features] 17 | trigger-overflow = [] 18 | -------------------------------------------------------------------------------- /ch4_interrupts/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch4_interrupts/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch4_interrupts/src/button.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::InputPin; 2 | use fugit::ExtU64; 3 | use microbit::hal::gpio::{Floating, Input, Pin}; 4 | 5 | use crate::{channel::Sender, time::Timer}; 6 | 7 | #[derive(Clone, Copy)] 8 | pub enum ButtonDirection { 9 | Left, 10 | Right, 11 | } 12 | 13 | enum ButtonState { 14 | WaitForPress, 15 | Debounce(Timer), 16 | } 17 | 18 | pub struct ButtonTask<'a> { 19 | pin: Pin>, 20 | direction: ButtonDirection, 21 | state: ButtonState, 22 | sender: Sender<'a, ButtonDirection>, 23 | } 24 | 25 | impl<'a> ButtonTask<'a> { 26 | pub fn new( 27 | pin: Pin>, 28 | direction: ButtonDirection, 29 | sender: Sender<'a, ButtonDirection>, 30 | ) -> Self { 31 | Self { 32 | pin, 33 | direction, 34 | state: ButtonState::WaitForPress, 35 | sender, 36 | } 37 | } 38 | 39 | pub fn poll(&mut self) { 40 | match self.state { 41 | ButtonState::WaitForPress => { 42 | if self.pin.is_low().unwrap() { 43 | self.sender.send(self.direction); 44 | self.state = ButtonState::Debounce(Timer::new(100.millis())); 45 | } 46 | } 47 | ButtonState::Debounce(ref timer) => { 48 | if timer.is_ready() && self.pin.is_high().unwrap() { 49 | self.state = ButtonState::WaitForPress; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ch4_interrupts/src/channel.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | 3 | pub struct Channel { 4 | item: Cell>, 5 | } 6 | 7 | impl Channel { 8 | pub fn new() -> Self { 9 | Self { 10 | item: Cell::new(None), 11 | } 12 | } 13 | 14 | pub fn get_sender(&self) -> Sender { 15 | Sender { channel: &self } 16 | } 17 | 18 | pub fn get_receiver(&self) -> Receiver { 19 | Receiver { channel: &self } 20 | } 21 | 22 | fn send(&self, item: T) { 23 | self.item.replace(Some(item)); 24 | } 25 | 26 | fn receive(&self) -> Option { 27 | self.item.take() 28 | } 29 | } 30 | 31 | pub struct Sender<'a, T> { 32 | channel: &'a Channel, 33 | } 34 | 35 | impl Sender<'_, T> { 36 | pub fn send(&self, item: T) { 37 | self.channel.send(item); 38 | } 39 | } 40 | 41 | pub struct Receiver<'a, T> { 42 | channel: &'a Channel, 43 | } 44 | 45 | impl Receiver<'_, T> { 46 | pub fn receive(&self) -> Option { 47 | self.channel.receive() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ch4_interrupts/src/led.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::{OutputPin, StatefulOutputPin}; 2 | use fugit::ExtU64; 3 | use microbit::{ 4 | gpio::NUM_COLS, 5 | hal::gpio::{Output, Pin, PushPull}, 6 | }; 7 | use rtt_target::rprintln; 8 | 9 | use crate::{button::ButtonDirection, channel::Receiver, time::Timer}; 10 | 11 | enum LedState { 12 | Toggle, 13 | Wait(Timer), 14 | } 15 | 16 | pub struct LedTask<'a> { 17 | col: [Pin>; NUM_COLS], 18 | active_col: usize, 19 | state: LedState, 20 | receiver: Receiver<'a, ButtonDirection>, 21 | } 22 | 23 | impl<'a> LedTask<'a> { 24 | pub fn new( 25 | col: [Pin>; NUM_COLS], 26 | receiver: Receiver<'a, ButtonDirection>, 27 | ) -> Self { 28 | Self { 29 | col, 30 | active_col: 0, 31 | state: LedState::Toggle, 32 | receiver, 33 | } 34 | } 35 | 36 | fn shift(&mut self, direction: ButtonDirection) { 37 | rprintln!("Button press detected.."); 38 | // switch off current/old LED 39 | self.col[self.active_col].set_high().ok(); 40 | self.active_col = match direction { 41 | ButtonDirection::Left => match self.active_col { 42 | 0 => NUM_COLS - 1, 43 | _ => self.active_col - 1, 44 | } 45 | ButtonDirection::Right => (self.active_col + 1) % NUM_COLS, 46 | }; 47 | // switch off new LED: moving to Toggle will then switch it on 48 | self.col[self.active_col].set_high().ok(); 49 | } 50 | 51 | fn toggle(&mut self) { 52 | rprintln!("Blinking LED {}", self.active_col); 53 | #[cfg(feature = "trigger-overflow")] 54 | { 55 | use crate::time::Ticker; 56 | let time = Ticker::now(); 57 | rprintln!( 58 | "Time: 0x{:x} ticks, {} ms", 59 | time.ticks(), 60 | time.duration_since_epoch().to_millis(), 61 | ); 62 | } 63 | self.col[self.active_col].toggle().ok(); 64 | } 65 | 66 | pub fn poll(&mut self) { 67 | match self.state { 68 | LedState::Toggle => { 69 | self.toggle(); 70 | self.state = LedState::Wait(Timer::new(500.millis())); 71 | } 72 | LedState::Wait(ref timer) => { 73 | if timer.is_ready() { 74 | self.state = LedState::Toggle; 75 | } 76 | if let Some(direction) = self.receiver.receive() { 77 | self.shift(direction); 78 | self.state = LedState::Toggle; 79 | } 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ch4_interrupts/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod button; 5 | mod channel; 6 | mod led; 7 | mod time; 8 | 9 | use button::{ButtonDirection, ButtonTask}; 10 | use channel::Channel; 11 | use cortex_m_rt::entry; 12 | use embedded_hal::digital::OutputPin; 13 | use led::LedTask; 14 | use microbit::Board; 15 | use panic_rtt_target as _; 16 | use rtt_target::rtt_init_print; 17 | use time::Ticker; 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | rtt_init_print!(); 22 | let mut board = Board::take().unwrap(); 23 | Ticker::init(board.RTC0, &mut board.NVIC); 24 | let (col, mut row) = board.display_pins.degrade(); 25 | row[0].set_high().ok(); 26 | let button_l = board.buttons.button_a.degrade(); 27 | let button_r = board.buttons.button_b.degrade(); 28 | 29 | let channel: Channel = Channel::new(); 30 | let mut led_task = LedTask::new(col, channel.get_receiver()); 31 | let mut button_l_task = ButtonTask::new(button_l, ButtonDirection::Left, channel.get_sender()); 32 | let mut button_r_task = ButtonTask::new(button_r, ButtonDirection::Right, channel.get_sender()); 33 | 34 | loop { 35 | led_task.poll(); 36 | button_l_task.poll(); 37 | button_r_task.poll(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ch4_interrupts/src/time.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::RefCell, 3 | sync::atomic::{AtomicU32, Ordering}, 4 | }; 5 | 6 | use critical_section::Mutex; 7 | use fugit::{Duration, Instant}; 8 | use microbit::{ 9 | hal::{rtc::RtcInterrupt, Rtc}, 10 | pac::{interrupt, NVIC, RTC0}, 11 | }; 12 | 13 | type TickInstant = Instant; 14 | type TickDuration = Duration; 15 | 16 | pub struct Timer { 17 | end_time: TickInstant, 18 | } 19 | 20 | impl Timer { 21 | pub fn new(duration: TickDuration) -> Self { 22 | Self { 23 | end_time: Ticker::now() + duration, 24 | } 25 | } 26 | 27 | pub fn is_ready(&self) -> bool { 28 | Ticker::now() >= self.end_time 29 | } 30 | } 31 | 32 | static TICKER: Ticker = Ticker { 33 | ovf_count: AtomicU32::new(0), 34 | rtc: Mutex::new(RefCell::new(None)), 35 | }; 36 | 37 | /// Keeps track of time for the system using RTC0, which ticks away at a rate 38 | /// of 32,768/sec using a low-power oscillator that runs even when the core is 39 | /// powered down. 40 | pub struct Ticker { 41 | ovf_count: AtomicU32, 42 | rtc: Mutex>>>, 43 | } 44 | 45 | impl Ticker { 46 | /// Called on startup to get RTC0 going, then hoists the HAL representation 47 | /// of RTC0 into the `static TICKER`, where it can be accessed by the 48 | /// interrupt handler function or any `TickTimer` instance. 49 | pub fn init(rtc0: RTC0, nvic: &mut NVIC) { 50 | let mut rtc = Rtc::new(rtc0, 0).unwrap(); 51 | rtc.enable_counter(); 52 | #[cfg(feature = "trigger-overflow")] 53 | { 54 | rtc.trigger_overflow(); 55 | // wait for the counter to initialize with its close-to-overflow 56 | // value before going any further, otherwise one of the tasks could 57 | // schedule a wakeup that will get skipped over when init happens. 58 | while rtc.get_counter() == 0 {} 59 | } 60 | rtc.enable_event(RtcInterrupt::Overflow); 61 | rtc.enable_interrupt(RtcInterrupt::Overflow, Some(nvic)); 62 | critical_section::with(|cs| { 63 | TICKER.rtc.replace(cs, Some(rtc)); 64 | }); 65 | } 66 | 67 | /// Current time is expressed as a TickInstant, which is a combination of: 68 | /// - The current RTC0 counter value (lowest 24 bits) 69 | /// - RTC0 counter overflow count (`ovf_count`, upper 40 bits) 70 | /// 71 | /// Extra care is needed to ensure the current overflow-count & counter 72 | /// value are collected during the same overflow-cycle. 73 | /// `Ordering::SeqCst` is used to prevent the compiler or processor from 74 | /// moving things around. 75 | pub fn now() -> TickInstant { 76 | let ticks = { 77 | loop { 78 | let ovf_before = TICKER.ovf_count.load(Ordering::SeqCst); 79 | let counter = critical_section::with(|cs| { 80 | TICKER.rtc.borrow_ref(cs).as_ref().unwrap().get_counter() 81 | }); 82 | let ovf = TICKER.ovf_count.load(Ordering::SeqCst); 83 | if ovf_before == ovf { 84 | break ((ovf as u64) << 24 | counter as u64); 85 | } 86 | } 87 | }; 88 | TickInstant::from_ticks(ticks) 89 | } 90 | } 91 | 92 | #[interrupt] 93 | fn RTC0() { 94 | critical_section::with(|cs| { 95 | let mut rm_rtc = TICKER.rtc.borrow_ref_mut(cs); 96 | let rtc = rm_rtc.as_mut().unwrap(); 97 | if rtc.is_event_triggered(RtcInterrupt::Overflow) { 98 | rtc.reset_event(RtcInterrupt::Overflow); 99 | TICKER.ovf_count.fetch_add(1, Ordering::Relaxed); 100 | } 101 | // Clearing the event flag can take up to 4 clock cycles: 102 | // (see nRF52833 Product Specification section 6.1.8) 103 | // this should do that... 104 | let _ = rtc.is_event_triggered(RtcInterrupt::Overflow); 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /ch5_futures/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch5_futures/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch5_futures/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "fugit" 146 | version = "0.3.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 149 | dependencies = [ 150 | "gcd", 151 | ] 152 | 153 | [[package]] 154 | name = "gcd" 155 | version = "2.3.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 158 | 159 | [[package]] 160 | name = "half" 161 | version = "2.4.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 164 | dependencies = [ 165 | "cfg-if", 166 | "crunchy", 167 | ] 168 | 169 | [[package]] 170 | name = "hash32" 171 | version = "0.3.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 174 | dependencies = [ 175 | "byteorder", 176 | ] 177 | 178 | [[package]] 179 | name = "heapless" 180 | version = "0.8.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 183 | dependencies = [ 184 | "hash32", 185 | "portable-atomic", 186 | "stable_deref_trait", 187 | ] 188 | 189 | [[package]] 190 | name = "microbit-common" 191 | version = "0.15.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 194 | dependencies = [ 195 | "embedded-hal 1.0.0", 196 | "nrf52833-hal", 197 | "tiny-led-matrix", 198 | ] 199 | 200 | [[package]] 201 | name = "microbit-v2" 202 | version = "0.15.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 205 | dependencies = [ 206 | "microbit-common", 207 | ] 208 | 209 | [[package]] 210 | name = "nb" 211 | version = "0.1.3" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 214 | dependencies = [ 215 | "nb 1.1.0", 216 | ] 217 | 218 | [[package]] 219 | name = "nb" 220 | version = "1.1.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 223 | 224 | [[package]] 225 | name = "nrf-hal-common" 226 | version = "0.18.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 229 | dependencies = [ 230 | "cast", 231 | "cfg-if", 232 | "cortex-m", 233 | "embedded-dma", 234 | "embedded-hal 1.0.0", 235 | "embedded-io", 236 | "embedded-storage", 237 | "fixed", 238 | "nb 1.1.0", 239 | "nrf-usbd", 240 | "nrf52833-pac", 241 | "rand_core", 242 | "void", 243 | ] 244 | 245 | [[package]] 246 | name = "nrf-usbd" 247 | version = "0.3.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 250 | dependencies = [ 251 | "cortex-m", 252 | "critical-section", 253 | "usb-device", 254 | "vcell", 255 | ] 256 | 257 | [[package]] 258 | name = "nrf52833-hal" 259 | version = "0.18.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 262 | dependencies = [ 263 | "nrf-hal-common", 264 | "nrf52833-pac", 265 | ] 266 | 267 | [[package]] 268 | name = "nrf52833-pac" 269 | version = "0.12.2" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 272 | dependencies = [ 273 | "cortex-m", 274 | "cortex-m-rt", 275 | "vcell", 276 | ] 277 | 278 | [[package]] 279 | name = "panic-rtt-target" 280 | version = "0.1.3" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 283 | dependencies = [ 284 | "critical-section", 285 | "rtt-target", 286 | ] 287 | 288 | [[package]] 289 | name = "portable-atomic" 290 | version = "1.7.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 293 | 294 | [[package]] 295 | name = "proc-macro2" 296 | version = "1.0.86" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 299 | dependencies = [ 300 | "unicode-ident", 301 | ] 302 | 303 | [[package]] 304 | name = "quote" 305 | version = "1.0.36" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 308 | dependencies = [ 309 | "proc-macro2", 310 | ] 311 | 312 | [[package]] 313 | name = "rand_core" 314 | version = "0.6.4" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 317 | 318 | [[package]] 319 | name = "rtt-target" 320 | version = "0.5.0" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 323 | dependencies = [ 324 | "critical-section", 325 | "ufmt-write", 326 | ] 327 | 328 | [[package]] 329 | name = "rustc_version" 330 | version = "0.2.3" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 333 | dependencies = [ 334 | "semver", 335 | ] 336 | 337 | [[package]] 338 | name = "semver" 339 | version = "0.9.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 342 | dependencies = [ 343 | "semver-parser", 344 | ] 345 | 346 | [[package]] 347 | name = "semver-parser" 348 | version = "0.7.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 351 | 352 | [[package]] 353 | name = "stable_deref_trait" 354 | version = "1.2.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 357 | 358 | [[package]] 359 | name = "syn" 360 | version = "1.0.109" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 363 | dependencies = [ 364 | "proc-macro2", 365 | "quote", 366 | "unicode-ident", 367 | ] 368 | 369 | [[package]] 370 | name = "tiny-led-matrix" 371 | version = "1.0.2" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 374 | 375 | [[package]] 376 | name = "typenum" 377 | version = "1.17.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 380 | 381 | [[package]] 382 | name = "ufmt-write" 383 | version = "0.1.0" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 386 | 387 | [[package]] 388 | name = "unicode-ident" 389 | version = "1.0.12" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 392 | 393 | [[package]] 394 | name = "usb-device" 395 | version = "0.3.2" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 398 | dependencies = [ 399 | "heapless", 400 | "portable-atomic", 401 | ] 402 | 403 | [[package]] 404 | name = "vcell" 405 | version = "0.1.3" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 408 | 409 | [[package]] 410 | name = "void" 411 | version = "1.0.2" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 414 | 415 | [[package]] 416 | name = "volatile-register" 417 | version = "0.2.2" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 420 | dependencies = [ 421 | "vcell", 422 | ] 423 | 424 | [[package]] 425 | name = "zero-to-async" 426 | version = "0.1.0" 427 | dependencies = [ 428 | "cortex-m", 429 | "cortex-m-rt", 430 | "critical-section", 431 | "embedded-hal 1.0.0", 432 | "fugit", 433 | "heapless", 434 | "microbit-v2", 435 | "panic-rtt-target", 436 | "rtt-target", 437 | ] 438 | -------------------------------------------------------------------------------- /ch5_futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | critical-section = "1.1.2" 10 | embedded-hal = "1.0.0" 11 | fugit = "0.3.7" 12 | heapless = { version = "0.8.0", features = ["portable-atomic"] } 13 | microbit-v2 = "0.15.0" 14 | panic-rtt-target = "0.1.3" 15 | rtt-target = "0.5.0" 16 | 17 | [features] 18 | trigger-overflow = [] 19 | -------------------------------------------------------------------------------- /ch5_futures/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch5_futures/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch5_futures/src/button.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::PinState; 2 | use fugit::ExtU64; 3 | use microbit::hal::{ 4 | gpio::{Floating, Input, Pin}, 5 | gpiote::Gpiote, 6 | }; 7 | 8 | use crate::{ 9 | channel::Sender, 10 | future::{OurFuture, Poll}, 11 | gpiote::InputChannel, 12 | time::Timer, 13 | }; 14 | 15 | #[derive(Clone, Copy)] 16 | pub enum ButtonDirection { 17 | Left, 18 | Right, 19 | } 20 | 21 | enum ButtonState { 22 | WaitForPress, 23 | Debounce(Timer), 24 | WaitForRelease, 25 | } 26 | 27 | pub struct ButtonTask<'a> { 28 | input: InputChannel, 29 | direction: ButtonDirection, 30 | state: ButtonState, 31 | sender: Sender<'a, ButtonDirection>, 32 | } 33 | 34 | impl<'a> ButtonTask<'a> { 35 | pub fn new( 36 | pin: Pin>, 37 | direction: ButtonDirection, 38 | sender: Sender<'a, ButtonDirection>, 39 | gpiote: &Gpiote, 40 | ) -> Self { 41 | Self { 42 | input: InputChannel::new(pin, gpiote), 43 | direction, 44 | state: ButtonState::WaitForPress, 45 | sender, 46 | } 47 | } 48 | } 49 | 50 | impl OurFuture for ButtonTask<'_> { 51 | type Output = (); 52 | fn poll(&mut self, task_id: usize) -> Poll { 53 | loop { 54 | match self.state { 55 | ButtonState::WaitForPress => { 56 | self.input.set_ready_state(PinState::Low); 57 | if let Poll::Ready(_) = self.input.poll(task_id) { 58 | self.sender.send(self.direction); 59 | self.state = ButtonState::Debounce(Timer::new(100.millis())); 60 | continue; 61 | } 62 | } 63 | ButtonState::Debounce(ref mut timer) => { 64 | if let Poll::Ready(_) = timer.poll(task_id) { 65 | self.state = ButtonState::WaitForRelease; 66 | continue; 67 | } 68 | } 69 | ButtonState::WaitForRelease => { 70 | self.input.set_ready_state(PinState::High); 71 | if let Poll::Ready(_) = self.input.poll(task_id) { 72 | self.state = ButtonState::WaitForPress; 73 | continue; 74 | } 75 | } 76 | } 77 | break; 78 | } 79 | Poll::Pending 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ch5_futures/src/channel.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | 3 | use crate::{ 4 | executor::wake_task, 5 | future::{OurFuture, Poll}, 6 | }; 7 | 8 | pub struct Channel { 9 | item: Cell>, 10 | task_id: Cell>, 11 | } 12 | 13 | impl Channel { 14 | pub fn new() -> Self { 15 | Self { 16 | item: Cell::new(None), 17 | task_id: Cell::new(None), 18 | } 19 | } 20 | 21 | pub fn get_sender(&self) -> Sender { 22 | Sender { channel: &self } 23 | } 24 | 25 | pub fn get_receiver(&self) -> Receiver { 26 | Receiver { 27 | channel: &self, 28 | state: ReceiverState::Init, 29 | } 30 | } 31 | 32 | fn send(&self, item: T) { 33 | self.item.replace(Some(item)); 34 | if let Some(task_id) = self.task_id.get() { 35 | wake_task(task_id); 36 | } 37 | } 38 | 39 | fn receive(&self) -> Option { 40 | self.item.take() 41 | } 42 | 43 | fn register(&self, task_id: usize) { 44 | self.task_id.replace(Some(task_id)); 45 | } 46 | } 47 | 48 | pub struct Sender<'a, T> { 49 | channel: &'a Channel, 50 | } 51 | 52 | impl Sender<'_, T> { 53 | pub fn send(&self, item: T) { 54 | self.channel.send(item); 55 | } 56 | } 57 | 58 | enum ReceiverState { 59 | Init, 60 | Wait, 61 | } 62 | 63 | pub struct Receiver<'a, T> { 64 | channel: &'a Channel, 65 | state: ReceiverState, 66 | } 67 | 68 | impl OurFuture for Receiver<'_, T> { 69 | type Output = T; 70 | fn poll(&mut self, task_id: usize) -> Poll { 71 | match self.state { 72 | ReceiverState::Init => { 73 | self.channel.register(task_id); 74 | self.state = ReceiverState::Wait; 75 | Poll::Pending 76 | } 77 | ReceiverState::Wait => match self.channel.receive() { 78 | Some(item) => Poll::Ready(item), 79 | None => Poll::Pending, 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ch5_futures/src/executor.rs: -------------------------------------------------------------------------------- 1 | use cortex_m::asm; 2 | use heapless::mpmc::Q4; 3 | use rtt_target::rprintln; 4 | 5 | use crate::future::OurFuture; 6 | 7 | pub fn wake_task(task_id: usize) { 8 | rprintln!("Waking task {}", task_id); 9 | if TASK_ID_READY.enqueue(task_id).is_err() { 10 | // Being unable to wake a task will likely cause it to become 11 | // permanently unresponsive... 12 | panic!("Task queue full: can't add task {}", task_id); 13 | } 14 | } 15 | 16 | static TASK_ID_READY: Q4 = Q4::new(); 17 | 18 | pub fn run_tasks(tasks: &mut [&mut dyn OurFuture]) -> ! { 19 | // everybody gets one run to start... 20 | for task_id in 0..tasks.len() { 21 | TASK_ID_READY.enqueue(task_id).ok(); 22 | } 23 | 24 | loop { 25 | while let Some(task_id) = TASK_ID_READY.dequeue() { 26 | if task_id >= tasks.len() { 27 | rprintln!("Bad task id {}!", task_id); 28 | continue; 29 | } 30 | rprintln!("Running task {}", task_id); 31 | tasks[task_id].poll(task_id); 32 | } 33 | rprintln!("No tasks ready, going to sleep..."); 34 | asm::wfi(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ch5_futures/src/future.rs: -------------------------------------------------------------------------------- 1 | /// Our simplified version of Rust's `core::future::Future` trait, used to get 2 | /// a feel for the architecture of an async runtime. 3 | pub trait OurFuture { 4 | type Output; 5 | fn poll(&mut self, task_id: usize) -> Poll; 6 | } 7 | 8 | /// Same as `core::task::Poll` 9 | /// Redefined here without all of the attribute clutter. 10 | pub enum Poll { 11 | Pending, 12 | Ready(T), 13 | } 14 | -------------------------------------------------------------------------------- /ch5_futures/src/gpiote.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | use embedded_hal::digital::{InputPin, PinState}; 4 | use microbit::{ 5 | hal::{ 6 | gpio::{Floating, Input, Pin}, 7 | gpiote::Gpiote, 8 | }, 9 | pac::{interrupt, Interrupt, NVIC}, 10 | }; 11 | 12 | use crate::{ 13 | executor::wake_task, 14 | future::{OurFuture, Poll}, 15 | }; 16 | 17 | const MAX_CHANNELS_USED: usize = 2; 18 | static NEXT_CHANNEL: AtomicUsize = AtomicUsize::new(0); 19 | 20 | pub struct InputChannel { 21 | pin: Pin>, 22 | channel_id: usize, 23 | ready_state: PinState, 24 | } 25 | 26 | impl InputChannel { 27 | pub fn new(pin: Pin>, gpiote: &Gpiote) -> Self { 28 | let channel_id = NEXT_CHANNEL.fetch_add(1, Ordering::Relaxed); 29 | let channel = match channel_id { 30 | 0 => gpiote.channel0(), 31 | 1 => gpiote.channel1(), 32 | MAX_CHANNELS_USED.. => todo!("Setup more channels!"), 33 | }; 34 | channel.input_pin(&pin).toggle().enable_interrupt(); 35 | // SAFETY: 36 | // We aren't using mask-based critical sections. 37 | unsafe { NVIC::unmask(Interrupt::GPIOTE); } 38 | Self { 39 | pin, 40 | channel_id, 41 | ready_state: PinState::Low, 42 | } 43 | } 44 | 45 | pub fn set_ready_state(&mut self, ready_state: PinState) { 46 | self.ready_state = ready_state; 47 | } 48 | } 49 | 50 | impl OurFuture for InputChannel { 51 | type Output = (); 52 | fn poll(&mut self, task_id: usize) -> Poll { 53 | if self.ready_state == PinState::from(self.pin.is_high().unwrap()) { 54 | Poll::Ready(()) 55 | } else { 56 | WAKE_TASKS[self.channel_id].store(task_id, Ordering::Relaxed); 57 | Poll::Pending 58 | } 59 | } 60 | } 61 | 62 | const INVALID_TASK_ID: usize = 0xFFFF_FFFF; 63 | const DEFAULT_TASK: AtomicUsize = AtomicUsize::new(INVALID_TASK_ID); 64 | static WAKE_TASKS: [AtomicUsize; MAX_CHANNELS_USED] = [DEFAULT_TASK; MAX_CHANNELS_USED]; 65 | 66 | #[interrupt] 67 | fn GPIOTE() { 68 | // SAFETY: 69 | // Use limited to `events_in` register, which is not accessed elsewhere. 70 | let gpiote = unsafe { &*microbit::pac::GPIOTE::ptr() }; 71 | for (channel, task) in WAKE_TASKS.iter().enumerate() { 72 | if gpiote.events_in[channel].read().bits() != 0 { 73 | gpiote.events_in[channel].write(|w| w); 74 | // Swap in the INVALID_TASK_ID to prevent the task-ready queue from 75 | // getting filled up during debounce. 76 | let task_id = task.swap(INVALID_TASK_ID, Ordering::Relaxed); 77 | if task_id != INVALID_TASK_ID { 78 | wake_task(task_id); 79 | } 80 | } 81 | } 82 | // Dummy read to ensure event flags clear 83 | // (see nRF52833 Product Specification section 6.1.8) 84 | let _ = gpiote.events_in[0].read().bits(); 85 | } 86 | -------------------------------------------------------------------------------- /ch5_futures/src/led.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::{OutputPin, StatefulOutputPin}; 2 | use fugit::ExtU64; 3 | use microbit::{ 4 | gpio::NUM_COLS, 5 | hal::gpio::{Output, Pin, PushPull}, 6 | }; 7 | use rtt_target::rprintln; 8 | 9 | use crate::{ 10 | button::ButtonDirection, 11 | channel::Receiver, 12 | future::{OurFuture, Poll}, 13 | time::Timer, 14 | }; 15 | 16 | enum LedState { 17 | Toggle, 18 | Wait(Timer), 19 | } 20 | 21 | pub struct LedTask<'a> { 22 | col: [Pin>; NUM_COLS], 23 | active_col: usize, 24 | state: LedState, 25 | receiver: Receiver<'a, ButtonDirection>, 26 | } 27 | 28 | impl<'a> LedTask<'a> { 29 | pub fn new( 30 | col: [Pin>; NUM_COLS], 31 | receiver: Receiver<'a, ButtonDirection>, 32 | ) -> Self { 33 | Self { 34 | col, 35 | active_col: 0, 36 | state: LedState::Toggle, 37 | receiver, 38 | } 39 | } 40 | 41 | fn shift(&mut self, direction: ButtonDirection) { 42 | rprintln!("Button press detected.."); 43 | // switch off current/old LED 44 | self.col[self.active_col].set_high().ok(); 45 | self.active_col = match direction { 46 | ButtonDirection::Left => match self.active_col { 47 | 0 => NUM_COLS - 1, 48 | _ => self.active_col - 1, 49 | } 50 | ButtonDirection::Right => (self.active_col + 1) % NUM_COLS, 51 | }; 52 | // switch off new LED: moving to Toggle will then switch it on 53 | self.col[self.active_col].set_high().ok(); 54 | } 55 | 56 | fn toggle(&mut self) { 57 | rprintln!("Blinking LED {}", self.active_col); 58 | #[cfg(feature = "trigger-overflow")] 59 | { 60 | use crate::time::Ticker; 61 | let time = Ticker::now(); 62 | rprintln!( 63 | "Time: 0x{:x} ticks, {} ms", 64 | time.ticks(), 65 | time.duration_since_epoch().to_millis(), 66 | ); 67 | } 68 | self.col[self.active_col].toggle().ok(); 69 | } 70 | } 71 | 72 | impl OurFuture for LedTask<'_> { 73 | type Output = (); 74 | fn poll(&mut self, task_id: usize) -> Poll { 75 | loop { 76 | match self.state { 77 | LedState::Toggle => { 78 | self.toggle(); 79 | self.state = LedState::Wait(Timer::new(500.millis())); 80 | } 81 | LedState::Wait(ref mut timer) => { 82 | if let Poll::Ready(_) = timer.poll(task_id) { 83 | self.state = LedState::Toggle; 84 | continue; 85 | } 86 | if let Poll::Ready(direction) = self.receiver.poll(task_id) { 87 | self.shift(direction); 88 | self.state = LedState::Toggle; 89 | continue; 90 | } 91 | break; 92 | } 93 | } 94 | } 95 | Poll::Pending 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ch5_futures/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod button; 5 | mod channel; 6 | mod executor; 7 | mod future; 8 | mod gpiote; 9 | mod led; 10 | mod time; 11 | 12 | use button::{ButtonDirection, ButtonTask}; 13 | use channel::Channel; 14 | use cortex_m_rt::entry; 15 | use embedded_hal::digital::OutputPin; 16 | use future::OurFuture; 17 | use led::LedTask; 18 | use microbit::{hal::gpiote::Gpiote, Board}; 19 | use panic_rtt_target as _; 20 | use rtt_target::rtt_init_print; 21 | use time::Ticker; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | rtt_init_print!(); 26 | let mut board = Board::take().unwrap(); 27 | Ticker::init(board.RTC0, &mut board.NVIC); 28 | let gpiote = Gpiote::new(board.GPIOTE); 29 | let (col, mut row) = board.display_pins.degrade(); 30 | row[0].set_high().ok(); 31 | let button_l = board.buttons.button_a.degrade(); 32 | let button_r = board.buttons.button_b.degrade(); 33 | 34 | let channel: Channel = Channel::new(); 35 | let mut led_task = LedTask::new(col, channel.get_receiver()); 36 | let mut button_l_task = ButtonTask::new( 37 | button_l, 38 | ButtonDirection::Left, 39 | channel.get_sender(), 40 | &gpiote 41 | ); 42 | let mut button_r_task = ButtonTask::new( 43 | button_r, 44 | ButtonDirection::Right, 45 | channel.get_sender(), 46 | &gpiote 47 | ); 48 | 49 | let mut tasks: [&mut dyn OurFuture; 3] = [ 50 | &mut led_task, 51 | &mut button_l_task, 52 | &mut button_r_task 53 | ]; 54 | executor::run_tasks(&mut tasks); 55 | } 56 | -------------------------------------------------------------------------------- /ch5_futures/src/time.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::{RefCell, RefMut}, 3 | sync::atomic::{AtomicU32, Ordering}, 4 | }; 5 | 6 | use critical_section::Mutex; 7 | use fugit::{Duration, Instant}; 8 | use heapless::{binary_heap::Min, BinaryHeap}; 9 | use microbit::{ 10 | hal::{ 11 | rtc::{RtcCompareReg, RtcInterrupt}, 12 | Rtc, 13 | }, 14 | pac::{interrupt, NVIC, RTC0}, 15 | }; 16 | 17 | use crate::{ 18 | executor::wake_task, 19 | future::{OurFuture, Poll}, 20 | }; 21 | 22 | type TickInstant = Instant; 23 | type TickDuration = Duration; 24 | 25 | const MAX_DEADLINES: usize = 8; 26 | static WAKE_DEADLINES: Mutex>> = 27 | Mutex::new(RefCell::new(BinaryHeap::new())); 28 | 29 | /// Deadlines can only be scheduled in a COMPARE register if they fall within 30 | /// the current overflow-cycle/epoch, and also are not too close to the current 31 | /// counter value. (see nRF52833 Product Specification section 6.20.7) 32 | fn schedule_wakeup( 33 | mut rm_deadlines: RefMut>, 34 | mut rm_rtc: RefMut>>, 35 | ) { 36 | let rtc = rm_rtc.as_mut().unwrap(); 37 | while let Some((deadline, task_id)) = rm_deadlines.peek() { 38 | let ovf_count = (*deadline >> 24) as u32; 39 | if ovf_count == TICKER.ovf_count.load(Ordering::Relaxed) { 40 | let counter = (*deadline & 0xFF_FF_FF) as u32; 41 | if counter > (rtc.get_counter() + 1) { 42 | rtc.set_compare(RtcCompareReg::Compare0, counter).ok(); 43 | rtc.enable_event(RtcInterrupt::Compare0); 44 | } else { 45 | // Wake now if it's too close or already past, 46 | // then try again with the next available deadline 47 | wake_task(*task_id); 48 | rm_deadlines.pop(); 49 | continue; 50 | } 51 | } 52 | break; 53 | } 54 | if rm_deadlines.is_empty() { 55 | rtc.disable_event(RtcInterrupt::Compare0); 56 | } 57 | } 58 | 59 | enum TimerState { 60 | Init, 61 | Wait, 62 | } 63 | 64 | pub struct Timer { 65 | end_time: TickInstant, 66 | state: TimerState, 67 | } 68 | 69 | impl Timer { 70 | pub fn new(duration: TickDuration) -> Self { 71 | Self { 72 | end_time: Ticker::now() + duration, 73 | state: TimerState::Init, 74 | } 75 | } 76 | 77 | /// Registration places the deadline & its task_id onto a `BinaryHeap`, and 78 | /// then will attempt to schedule it via COMPARE0 if it's earlier than 79 | /// the current deadline. 80 | fn register(&self, task_id: usize) { 81 | let new_deadline = self.end_time.ticks(); 82 | critical_section::with(|cs| { 83 | let mut rm_deadlines = WAKE_DEADLINES.borrow_ref_mut(cs); 84 | let is_earliest = if let Some((next_deadline, _)) = rm_deadlines.peek() { 85 | new_deadline < *next_deadline 86 | } else { 87 | true 88 | }; 89 | if rm_deadlines.push((new_deadline, task_id)).is_err() { 90 | // Dropping a deadline in this system can be Very Bad: 91 | // - In the LED task, the LED will stop updating, but may come 92 | // back to life on a button press... 93 | // - In a button task, it may never wake again 94 | // `panic` to raise awareness of the issue during development 95 | panic!("Deadline dropped for task {}!", task_id); 96 | } 97 | // schedule now if its the earliest 98 | if is_earliest { 99 | schedule_wakeup(rm_deadlines, TICKER.rtc.borrow_ref_mut(cs)); 100 | } 101 | }); 102 | } 103 | } 104 | 105 | impl OurFuture for Timer { 106 | type Output = (); 107 | fn poll(&mut self, task_id: usize) -> Poll { 108 | match self.state { 109 | TimerState::Init => { 110 | self.register(task_id); 111 | self.state = TimerState::Wait; 112 | Poll::Pending 113 | } 114 | TimerState::Wait => { 115 | if Ticker::now() >= self.end_time { 116 | Poll::Ready(()) 117 | } else { 118 | Poll::Pending 119 | } 120 | } 121 | } 122 | } 123 | } 124 | 125 | static TICKER: Ticker = Ticker { 126 | ovf_count: AtomicU32::new(0), 127 | rtc: Mutex::new(RefCell::new(None)), 128 | }; 129 | 130 | /// Keeps track of time for the system using RTC0, which ticks away at a rate 131 | /// of 32,768/sec using a low-power oscillator that runs even when the core is 132 | /// powered down. 133 | pub struct Ticker { 134 | ovf_count: AtomicU32, 135 | rtc: Mutex>>>, 136 | } 137 | 138 | impl Ticker { 139 | /// Called on startup to get RTC0 going, then hoists the HAL representation 140 | /// of RTC0 into the `static TICKER`, where it can be accessed by the 141 | /// interrupt handler function or any `Timer` instance. 142 | pub fn init(rtc0: RTC0, nvic: &mut NVIC) { 143 | let mut rtc = Rtc::new(rtc0, 0).unwrap(); 144 | rtc.enable_counter(); 145 | #[cfg(feature = "trigger-overflow")] 146 | { 147 | rtc.trigger_overflow(); 148 | // wait for the counter to initialize with its close-to-overflow 149 | // value before going any further, otherwise one of the tasks could 150 | // schedule a wakeup that will get skipped over when init happens. 151 | while rtc.get_counter() == 0 {} 152 | } 153 | rtc.enable_event(RtcInterrupt::Overflow); 154 | rtc.enable_interrupt(RtcInterrupt::Overflow, Some(nvic)); 155 | rtc.enable_interrupt(RtcInterrupt::Compare0, Some(nvic)); 156 | critical_section::with(|cs| { 157 | TICKER.rtc.replace(cs, Some(rtc)); 158 | }); 159 | } 160 | 161 | /// Current time is expressed as a TickInstant, which is a combination of: 162 | /// - The current RTC0 counter value (lowest 24 bits) 163 | /// - RTC0 counter overflow count (`ovf_count`, upper 40 bits) 164 | /// 165 | /// Extra care is needed to ensure the current overflow-count & counter 166 | /// value are collected during the same overflow-cycle. 167 | /// `Ordering::SeqCst` is used to prevent the compiler or processor from 168 | /// moving things around. 169 | pub fn now() -> TickInstant { 170 | let ticks = { 171 | loop { 172 | let ovf_before = TICKER.ovf_count.load(Ordering::SeqCst); 173 | let counter = critical_section::with(|cs| { 174 | TICKER.rtc.borrow_ref(cs).as_ref().unwrap().get_counter() 175 | }); 176 | let ovf = TICKER.ovf_count.load(Ordering::SeqCst); 177 | if ovf_before == ovf { 178 | break ((ovf as u64) << 24 | counter as u64); 179 | } 180 | } 181 | }; 182 | TickInstant::from_ticks(ticks) 183 | } 184 | } 185 | 186 | #[interrupt] 187 | fn RTC0() { 188 | critical_section::with(|cs| { 189 | let mut rm_rtc = TICKER.rtc.borrow_ref_mut(cs); 190 | let rtc = rm_rtc.as_mut().unwrap(); 191 | if rtc.is_event_triggered(RtcInterrupt::Overflow) { 192 | rtc.reset_event(RtcInterrupt::Overflow); 193 | TICKER.ovf_count.fetch_add(1, Ordering::Relaxed); 194 | } 195 | if rtc.is_event_triggered(RtcInterrupt::Compare0) { 196 | rtc.reset_event(RtcInterrupt::Compare0); 197 | } 198 | 199 | // For OVF & COMPARE0 events, schedule the next wakeup. This should also 200 | // kill enough clock cycles to allow the event flags to clear. 201 | // (see nRF52833 Product Specification section 6.1.8) 202 | schedule_wakeup(WAKE_DEADLINES.borrow_ref_mut(cs), rm_rtc); 203 | }); 204 | } 205 | -------------------------------------------------------------------------------- /ch6_async_await/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch6_async_await/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch6_async_await/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "az" 7 | version = "1.2.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "0.2.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 16 | dependencies = [ 17 | "rustc_version", 18 | ] 19 | 20 | [[package]] 21 | name = "bitfield" 22 | version = "0.13.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 25 | 26 | [[package]] 27 | name = "bytemuck" 28 | version = "1.16.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" 31 | 32 | [[package]] 33 | name = "byteorder" 34 | version = "1.5.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 37 | 38 | [[package]] 39 | name = "cast" 40 | version = "0.3.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn 1.0.109", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "embedded-dma" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" 100 | dependencies = [ 101 | "stable_deref_trait", 102 | ] 103 | 104 | [[package]] 105 | name = "embedded-hal" 106 | version = "0.2.7" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 109 | dependencies = [ 110 | "nb 0.1.3", 111 | "void", 112 | ] 113 | 114 | [[package]] 115 | name = "embedded-hal" 116 | version = "1.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 119 | 120 | [[package]] 121 | name = "embedded-io" 122 | version = "0.6.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 125 | 126 | [[package]] 127 | name = "embedded-storage" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 131 | 132 | [[package]] 133 | name = "fixed" 134 | version = "1.28.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 137 | dependencies = [ 138 | "az", 139 | "bytemuck", 140 | "half", 141 | "typenum", 142 | ] 143 | 144 | [[package]] 145 | name = "fugit" 146 | version = "0.3.7" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 149 | dependencies = [ 150 | "gcd", 151 | ] 152 | 153 | [[package]] 154 | name = "futures" 155 | version = "0.3.30" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 158 | dependencies = [ 159 | "futures-channel", 160 | "futures-core", 161 | "futures-io", 162 | "futures-sink", 163 | "futures-task", 164 | "futures-util", 165 | ] 166 | 167 | [[package]] 168 | name = "futures-channel" 169 | version = "0.3.30" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 172 | dependencies = [ 173 | "futures-core", 174 | "futures-sink", 175 | ] 176 | 177 | [[package]] 178 | name = "futures-core" 179 | version = "0.3.30" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 182 | 183 | [[package]] 184 | name = "futures-io" 185 | version = "0.3.30" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 188 | 189 | [[package]] 190 | name = "futures-macro" 191 | version = "0.3.30" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 194 | dependencies = [ 195 | "proc-macro2", 196 | "quote", 197 | "syn 2.0.72", 198 | ] 199 | 200 | [[package]] 201 | name = "futures-sink" 202 | version = "0.3.30" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 205 | 206 | [[package]] 207 | name = "futures-task" 208 | version = "0.3.30" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 211 | 212 | [[package]] 213 | name = "futures-util" 214 | version = "0.3.30" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 217 | dependencies = [ 218 | "futures-core", 219 | "futures-macro", 220 | "futures-sink", 221 | "futures-task", 222 | "pin-project-lite", 223 | "pin-utils", 224 | ] 225 | 226 | [[package]] 227 | name = "gcd" 228 | version = "2.3.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 231 | 232 | [[package]] 233 | name = "half" 234 | version = "2.4.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 237 | dependencies = [ 238 | "cfg-if", 239 | "crunchy", 240 | ] 241 | 242 | [[package]] 243 | name = "hash32" 244 | version = "0.3.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 247 | dependencies = [ 248 | "byteorder", 249 | ] 250 | 251 | [[package]] 252 | name = "heapless" 253 | version = "0.8.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 256 | dependencies = [ 257 | "hash32", 258 | "portable-atomic", 259 | "stable_deref_trait", 260 | ] 261 | 262 | [[package]] 263 | name = "microbit-common" 264 | version = "0.15.0" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "26d84d7858bbe6cdd89c429be9aa62f2820d1a271e66580df22fe9093bb63c51" 267 | dependencies = [ 268 | "embedded-hal 1.0.0", 269 | "nrf52833-hal", 270 | "tiny-led-matrix", 271 | ] 272 | 273 | [[package]] 274 | name = "microbit-v2" 275 | version = "0.15.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "0729395e7ffd39a515e9e5bcaacc282623ad7630fbe5c39b7a0fa656ec24918b" 278 | dependencies = [ 279 | "microbit-common", 280 | ] 281 | 282 | [[package]] 283 | name = "nb" 284 | version = "0.1.3" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 287 | dependencies = [ 288 | "nb 1.1.0", 289 | ] 290 | 291 | [[package]] 292 | name = "nb" 293 | version = "1.1.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 296 | 297 | [[package]] 298 | name = "nrf-hal-common" 299 | version = "0.18.0" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "0c134de1f2f0191aed3fc24d3831da8808d1e636b06edf81a5717103095f625d" 302 | dependencies = [ 303 | "cast", 304 | "cfg-if", 305 | "cortex-m", 306 | "embedded-dma", 307 | "embedded-hal 1.0.0", 308 | "embedded-io", 309 | "embedded-storage", 310 | "fixed", 311 | "nb 1.1.0", 312 | "nrf-usbd", 313 | "nrf52833-pac", 314 | "rand_core", 315 | "void", 316 | ] 317 | 318 | [[package]] 319 | name = "nrf-usbd" 320 | version = "0.3.0" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "aedf862f941154442271ae9914777bd1c93f6d2e0dc9db4cafa160e55ffb9085" 323 | dependencies = [ 324 | "cortex-m", 325 | "critical-section", 326 | "usb-device", 327 | "vcell", 328 | ] 329 | 330 | [[package]] 331 | name = "nrf52833-hal" 332 | version = "0.18.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "12fab00e54c93792dddaf623a021feaa84933bbad12c3b76f08ea201df91f36c" 335 | dependencies = [ 336 | "nrf-hal-common", 337 | "nrf52833-pac", 338 | ] 339 | 340 | [[package]] 341 | name = "nrf52833-pac" 342 | version = "0.12.2" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 345 | dependencies = [ 346 | "cortex-m", 347 | "cortex-m-rt", 348 | "vcell", 349 | ] 350 | 351 | [[package]] 352 | name = "panic-rtt-target" 353 | version = "0.1.3" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 356 | dependencies = [ 357 | "critical-section", 358 | "rtt-target", 359 | ] 360 | 361 | [[package]] 362 | name = "pin-project-lite" 363 | version = "0.2.14" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 366 | 367 | [[package]] 368 | name = "pin-utils" 369 | version = "0.1.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 372 | 373 | [[package]] 374 | name = "portable-atomic" 375 | version = "1.7.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 378 | 379 | [[package]] 380 | name = "proc-macro2" 381 | version = "1.0.86" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 384 | dependencies = [ 385 | "unicode-ident", 386 | ] 387 | 388 | [[package]] 389 | name = "quote" 390 | version = "1.0.36" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 393 | dependencies = [ 394 | "proc-macro2", 395 | ] 396 | 397 | [[package]] 398 | name = "rand_core" 399 | version = "0.6.4" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 402 | 403 | [[package]] 404 | name = "rtt-target" 405 | version = "0.5.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 408 | dependencies = [ 409 | "critical-section", 410 | "ufmt-write", 411 | ] 412 | 413 | [[package]] 414 | name = "rustc_version" 415 | version = "0.2.3" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 418 | dependencies = [ 419 | "semver", 420 | ] 421 | 422 | [[package]] 423 | name = "semver" 424 | version = "0.9.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 427 | dependencies = [ 428 | "semver-parser", 429 | ] 430 | 431 | [[package]] 432 | name = "semver-parser" 433 | version = "0.7.0" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 436 | 437 | [[package]] 438 | name = "stable_deref_trait" 439 | version = "1.2.0" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 442 | 443 | [[package]] 444 | name = "syn" 445 | version = "1.0.109" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 448 | dependencies = [ 449 | "proc-macro2", 450 | "quote", 451 | "unicode-ident", 452 | ] 453 | 454 | [[package]] 455 | name = "syn" 456 | version = "2.0.72" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" 459 | dependencies = [ 460 | "proc-macro2", 461 | "quote", 462 | "unicode-ident", 463 | ] 464 | 465 | [[package]] 466 | name = "tiny-led-matrix" 467 | version = "1.0.2" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "a718c727b686154a7c7913f70d7ebc8956f701cbab466bc22035cb27f378882b" 470 | 471 | [[package]] 472 | name = "typenum" 473 | version = "1.17.0" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 476 | 477 | [[package]] 478 | name = "ufmt-write" 479 | version = "0.1.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 482 | 483 | [[package]] 484 | name = "unicode-ident" 485 | version = "1.0.12" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 488 | 489 | [[package]] 490 | name = "usb-device" 491 | version = "0.3.2" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" 494 | dependencies = [ 495 | "heapless", 496 | "portable-atomic", 497 | ] 498 | 499 | [[package]] 500 | name = "vcell" 501 | version = "0.1.3" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 504 | 505 | [[package]] 506 | name = "void" 507 | version = "1.0.2" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 510 | 511 | [[package]] 512 | name = "volatile-register" 513 | version = "0.2.2" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 516 | dependencies = [ 517 | "vcell", 518 | ] 519 | 520 | [[package]] 521 | name = "zero-to-async" 522 | version = "0.1.0" 523 | dependencies = [ 524 | "cortex-m", 525 | "cortex-m-rt", 526 | "critical-section", 527 | "embedded-hal 1.0.0", 528 | "fugit", 529 | "futures", 530 | "heapless", 531 | "microbit-v2", 532 | "panic-rtt-target", 533 | "rtt-target", 534 | ] 535 | -------------------------------------------------------------------------------- /ch6_async_await/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | critical-section = "1.1.2" 10 | embedded-hal = "1.0.0" 11 | fugit = "0.3.7" 12 | futures = { version = "0.3.30", default-features = false, features = [ 13 | "async-await", 14 | ] } 15 | heapless = { version = "0.8.0", features = ["portable-atomic"] } 16 | microbit-v2 = "0.15.0" 17 | panic-rtt-target = "0.1.3" 18 | rtt-target = "0.5.0" 19 | 20 | [features] 21 | trigger-overflow = [] 22 | -------------------------------------------------------------------------------- /ch6_async_await/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch6_async_await/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch6_async_await/src/button.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub enum ButtonDirection { 3 | Left, 4 | Right, 5 | } 6 | -------------------------------------------------------------------------------- /ch6_async_await/src/channel.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::{Cell, RefCell}, 3 | future::poll_fn, 4 | task::{Poll, Waker}, 5 | }; 6 | 7 | /// Storing the `Waker` directly this time, just to see how that works. 8 | /// There is no more executor dependency, which is nice.. 9 | pub struct Channel { 10 | item: Cell>, 11 | waker: RefCell>, 12 | } 13 | 14 | impl Channel { 15 | pub fn new() -> Self { 16 | Self { 17 | item: Cell::new(None), 18 | waker: RefCell::new(None), 19 | } 20 | } 21 | 22 | pub fn get_sender(&self) -> Sender { 23 | Sender { channel: &self } 24 | } 25 | 26 | pub fn get_receiver(&self) -> Receiver { 27 | Receiver { 28 | channel: &self, 29 | state: ReceiverState::Init, 30 | } 31 | } 32 | 33 | fn send(&self, item: T) { 34 | self.item.replace(Some(item)); 35 | if let Some(waker) = self.waker.borrow().as_ref() { 36 | // Calling `wake()` consumes the waker, which means we'd have to 37 | // `clone()` it first, so instead here we use `wake_by_ref()` 38 | waker.wake_by_ref(); 39 | } 40 | } 41 | 42 | fn receive(&self) -> Option { 43 | self.item.take() 44 | } 45 | 46 | fn register(&self, waker: Waker) { 47 | self.waker.replace(Some(waker)); 48 | } 49 | } 50 | 51 | pub struct Sender<'a, T> { 52 | channel: &'a Channel, 53 | } 54 | 55 | impl Sender<'_, T> { 56 | pub fn send(&self, item: T) { 57 | self.channel.send(item); 58 | } 59 | } 60 | 61 | enum ReceiverState { 62 | Init, 63 | Wait, 64 | } 65 | 66 | pub struct Receiver<'a, T> { 67 | channel: &'a Channel, 68 | state: ReceiverState, 69 | } 70 | 71 | impl Receiver<'_, T> { 72 | pub async fn receive(&mut self) -> T { 73 | poll_fn(|cx| match self.state { 74 | ReceiverState::Init => { 75 | self.channel.register(cx.waker().clone()); 76 | self.state = ReceiverState::Wait; 77 | Poll::Pending 78 | } 79 | ReceiverState::Wait => match self.channel.receive() { 80 | Some(item) => Poll::Ready(item), 81 | None => Poll::Pending, 82 | } 83 | }) 84 | .await 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /ch6_async_await/src/executor.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | future::Future, 3 | pin::Pin, 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | task::{Context, RawWaker, RawWakerVTable, Waker}, 6 | }; 7 | 8 | use cortex_m::asm; 9 | use heapless::mpmc::Q4; 10 | use rtt_target::rprintln; 11 | 12 | /// An alternative to storing the waker: just extract the task information 13 | /// you're looking for via an extension trait that you can implement for `Waker` 14 | /// Not a great general solution if you want to be compatible with other 15 | /// executors, but for this project it's fine. 16 | pub trait ExtWaker { 17 | fn task_id(&self) -> usize; 18 | } 19 | 20 | impl ExtWaker for Waker { 21 | fn task_id(&self) -> usize { 22 | // When "waker-getters" is stabilized, do this instead: 23 | // self.as_raw().data() as usize 24 | for task_id in 0..NUM_TASKS.load(Ordering::Relaxed) { 25 | if get_waker(task_id).will_wake(self) { 26 | return task_id; 27 | } 28 | } 29 | panic!("Unknown waker/executor!"); 30 | } 31 | } 32 | 33 | fn get_waker(task_id: usize) -> Waker { 34 | // SAFETY: 35 | // Data argument interpreted as an integer, not dereferenced 36 | unsafe { 37 | Waker::from_raw(RawWaker::new(task_id as *const (), &VTABLE)) 38 | } 39 | } 40 | 41 | static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); 42 | 43 | unsafe fn clone(p: *const ()) -> RawWaker { 44 | RawWaker::new(p, &VTABLE) 45 | } 46 | 47 | unsafe fn drop(_p: *const ()) {} 48 | 49 | unsafe fn wake(p: *const ()) { 50 | wake_task(p as usize); 51 | } 52 | 53 | unsafe fn wake_by_ref(p: *const ()) { 54 | wake_task(p as usize); 55 | } 56 | 57 | pub fn wake_task(task_id: usize) { 58 | rprintln!("Waking task {}", task_id); 59 | if TASK_ID_READY.enqueue(task_id).is_err() { 60 | // Being unable to wake a task will likely cause it to become 61 | // permanently unresponsive. 62 | panic!("Task queue full: can't add task {}", task_id); 63 | } 64 | } 65 | 66 | static TASK_ID_READY: Q4 = Q4::new(); 67 | static NUM_TASKS: AtomicUsize = AtomicUsize::new(0); 68 | 69 | pub fn run_tasks(tasks: &mut [Pin<&mut dyn Future>]) -> ! { 70 | NUM_TASKS.store(tasks.len(), Ordering::Relaxed); 71 | 72 | // everybody gets one run to start... 73 | for task_id in 0..tasks.len() { 74 | TASK_ID_READY.enqueue(task_id).ok(); 75 | } 76 | 77 | loop { 78 | while let Some(task_id) = TASK_ID_READY.dequeue() { 79 | if task_id >= tasks.len() { 80 | rprintln!("Bad task id {}!", task_id); 81 | continue; 82 | } 83 | rprintln!("Running task {}", task_id); 84 | let _ = tasks[task_id] 85 | .as_mut() 86 | .poll(&mut Context::from_waker(&get_waker(task_id))); 87 | } 88 | rprintln!("No tasks ready, going to sleep..."); 89 | asm::wfi(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ch6_async_await/src/gpiote.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | future::poll_fn, 3 | sync::atomic::{AtomicUsize, Ordering}, 4 | task::Poll, 5 | }; 6 | 7 | use embedded_hal::digital::{InputPin, PinState}; 8 | use microbit::{ 9 | hal::{ 10 | gpio::{Floating, Input, Pin}, 11 | gpiote::Gpiote, 12 | }, 13 | pac::{interrupt, Interrupt, NVIC}, 14 | }; 15 | 16 | use crate::executor::{wake_task, ExtWaker}; 17 | 18 | const MAX_CHANNELS_USED: usize = 2; 19 | static NEXT_CHANNEL: AtomicUsize = AtomicUsize::new(0); 20 | 21 | pub struct InputChannel { 22 | pin: Pin>, 23 | channel_id: usize, 24 | } 25 | 26 | impl InputChannel { 27 | pub fn new(pin: Pin>, gpiote: &Gpiote) -> Self { 28 | let channel_id = NEXT_CHANNEL.fetch_add(1, Ordering::Relaxed); 29 | let channel = match channel_id { 30 | 0 => gpiote.channel0(), 31 | 1 => gpiote.channel1(), 32 | MAX_CHANNELS_USED.. => todo!("Setup more channels!"), 33 | }; 34 | channel.input_pin(&pin).toggle().enable_interrupt(); 35 | // SAFETY: 36 | // We aren't using mask-based critical sections. 37 | unsafe { NVIC::unmask(Interrupt::GPIOTE); } 38 | Self { 39 | pin, 40 | channel_id, 41 | } 42 | } 43 | 44 | pub async fn wait_for(&mut self, ready_state: PinState) { 45 | poll_fn(|cx| { 46 | if ready_state == PinState::from(self.pin.is_high().unwrap()) { 47 | Poll::Ready(()) 48 | } else { 49 | WAKE_TASKS[self.channel_id].store(cx.waker().task_id(), Ordering::Relaxed); 50 | Poll::Pending 51 | } 52 | }) 53 | .await 54 | } 55 | } 56 | 57 | const INVALID_TASK_ID: usize = 0xFFFF_FFFF; 58 | const DEFAULT_TASK: AtomicUsize = AtomicUsize::new(INVALID_TASK_ID); 59 | static WAKE_TASKS: [AtomicUsize; MAX_CHANNELS_USED] = [DEFAULT_TASK; MAX_CHANNELS_USED]; 60 | 61 | #[interrupt] 62 | fn GPIOTE() { 63 | // SAFETY: 64 | // Use limited to `events_in` register, which is not accessed elsewhere. 65 | let gpiote = unsafe { &*microbit::pac::GPIOTE::ptr() }; 66 | for (channel, task) in WAKE_TASKS.iter().enumerate() { 67 | if gpiote.events_in[channel].read().bits() != 0 { 68 | gpiote.events_in[channel].write(|w| w); 69 | // Swap in the INVALID_TASK_ID to prevent the task-ready queue from 70 | // getting filled up during debounce. 71 | let task_id = task.swap(INVALID_TASK_ID, Ordering::Relaxed); 72 | if task_id != INVALID_TASK_ID { 73 | wake_task(task_id); 74 | } 75 | } 76 | } 77 | // Dummy read to ensure event flags clear 78 | // (see nRF52833 Product Specification section 6.1.8) 79 | let _ = gpiote.events_in[0].read().bits(); 80 | } 81 | -------------------------------------------------------------------------------- /ch6_async_await/src/led.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::{OutputPin, StatefulOutputPin}; 2 | use microbit::{ 3 | gpio::NUM_COLS, 4 | hal::gpio::{Output, Pin, PushPull}, 5 | }; 6 | use rtt_target::rprintln; 7 | 8 | use crate::button::ButtonDirection; 9 | 10 | pub struct LedRow { 11 | col: [Pin>; NUM_COLS], 12 | active_col: usize, 13 | } 14 | 15 | impl LedRow { 16 | pub fn new( 17 | col: [Pin>; NUM_COLS], 18 | ) -> Self { 19 | Self { 20 | col, 21 | active_col: 0, 22 | } 23 | } 24 | 25 | pub fn shift(&mut self, direction: ButtonDirection) { 26 | rprintln!("Button press detected.."); 27 | // switch off current/old LED 28 | self.col[self.active_col].set_high().ok(); 29 | self.active_col = match direction { 30 | ButtonDirection::Left => match self.active_col { 31 | 0 => NUM_COLS - 1, 32 | _ => self.active_col - 1, 33 | } 34 | ButtonDirection::Right => (self.active_col + 1) % NUM_COLS, 35 | }; 36 | // switch off new LED: moving to Toggle will then switch it on 37 | self.col[self.active_col].set_high().ok(); 38 | } 39 | 40 | pub fn toggle(&mut self) { 41 | rprintln!("Blinking LED {}", self.active_col); 42 | #[cfg(feature = "trigger-overflow")] 43 | { 44 | use crate::time::Ticker; 45 | let time = Ticker::now(); 46 | rprintln!( 47 | "Time: 0x{:x} ticks, {} ms", 48 | time.ticks(), 49 | time.duration_since_epoch().to_millis(), 50 | ); 51 | } 52 | self.col[self.active_col].toggle().ok(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ch6_async_await/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod button; 5 | mod channel; 6 | mod executor; 7 | mod gpiote; 8 | mod led; 9 | mod time; 10 | 11 | use core::pin::pin; 12 | 13 | use button::ButtonDirection; 14 | use channel::{Channel, Receiver, Sender}; 15 | use cortex_m_rt::entry; 16 | use embedded_hal::digital::{OutputPin, PinState}; 17 | use fugit::ExtU64; 18 | use futures::{select_biased, FutureExt}; 19 | use gpiote::InputChannel; 20 | use led::LedRow; 21 | use microbit::{ 22 | gpio::NUM_COLS, 23 | hal::{ 24 | gpio::{Floating, Input, Output, Pin, PushPull}, 25 | gpiote::Gpiote, 26 | }, 27 | Board, 28 | }; 29 | use panic_rtt_target as _; 30 | use rtt_target::rtt_init_print; 31 | use time::Ticker; 32 | 33 | #[entry] 34 | fn main() -> ! { 35 | rtt_init_print!(); 36 | let mut board = Board::take().unwrap(); 37 | Ticker::init(board.RTC0, &mut board.NVIC); 38 | let gpiote = Gpiote::new(board.GPIOTE); 39 | let (col, mut row) = board.display_pins.degrade(); 40 | row[0].set_high().ok(); 41 | let button_l = board.buttons.button_a.degrade(); 42 | let button_r = board.buttons.button_b.degrade(); 43 | 44 | let channel: Channel = Channel::new(); 45 | let led_task = pin!(led_task(col, channel.get_receiver())); 46 | let button_l_task = pin!(button_task( 47 | button_l, 48 | ButtonDirection::Left, 49 | channel.get_sender(), 50 | &gpiote 51 | )); 52 | let button_r_task = pin!(button_task( 53 | button_r, 54 | ButtonDirection::Right, 55 | channel.get_sender(), 56 | &gpiote 57 | )); 58 | 59 | executor::run_tasks(&mut [led_task, button_l_task, button_r_task]); 60 | } 61 | 62 | async fn led_task( 63 | col: [Pin>; NUM_COLS], 64 | mut receiver: Receiver<'_, ButtonDirection>, 65 | ) { 66 | let mut blinker = LedRow::new(col); 67 | loop { 68 | blinker.toggle(); 69 | select_biased! { 70 | direction = receiver.receive().fuse() => { 71 | blinker.shift(direction); 72 | } 73 | _ = time::delay(500.millis()).fuse() => {} 74 | } 75 | } 76 | } 77 | 78 | async fn button_task( 79 | pin: Pin>, 80 | direction: ButtonDirection, 81 | sender: Sender<'_, ButtonDirection>, 82 | gpiote: &Gpiote, 83 | ) { 84 | let mut input = InputChannel::new(pin, gpiote); 85 | loop { 86 | input.wait_for(PinState::Low).await; 87 | sender.send(direction); 88 | time::delay(100.millis()).await; 89 | input.wait_for(PinState::High).await; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ch6_async_await/src/time.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cell::{RefCell, RefMut}, 3 | future::Future, 4 | pin::Pin, 5 | sync::atomic::{AtomicU32, Ordering}, 6 | task::{Context, Poll}, 7 | }; 8 | 9 | use critical_section::Mutex; 10 | use fugit::{Duration, Instant}; 11 | use heapless::{binary_heap::Min, BinaryHeap}; 12 | use microbit::{ 13 | hal::{ 14 | rtc::{RtcCompareReg, RtcInterrupt}, 15 | Rtc, 16 | }, 17 | pac::{interrupt, NVIC, RTC0}, 18 | }; 19 | 20 | use crate::executor::{wake_task, ExtWaker}; 21 | 22 | type TickInstant = Instant; 23 | type TickDuration = Duration; 24 | 25 | const MAX_DEADLINES: usize = 8; 26 | static WAKE_DEADLINES: Mutex>> = 27 | Mutex::new(RefCell::new(BinaryHeap::new())); 28 | 29 | /// Deadlines can only be scheduled in a COMPARE register if they fall within 30 | /// the current overflow-cycle/epoch, and also are not too close to the current 31 | /// counter value. (see nRF52833 Product Specification section 6.20.7) 32 | fn schedule_wakeup( 33 | mut rm_deadlines: RefMut>, 34 | mut rm_rtc: RefMut>>, 35 | ) { 36 | let rtc = rm_rtc.as_mut().unwrap(); 37 | while let Some((deadline, task_id)) = rm_deadlines.peek() { 38 | let ovf_count = (*deadline >> 24) as u32; 39 | if ovf_count == TICKER.ovf_count.load(Ordering::Relaxed) { 40 | let counter = (*deadline & 0xFF_FF_FF) as u32; 41 | if counter > (rtc.get_counter() + 1) { 42 | rtc.set_compare(RtcCompareReg::Compare0, counter).ok(); 43 | rtc.enable_event(RtcInterrupt::Compare0); 44 | } else { 45 | // Wake now if it's too close or already past, 46 | // then try again with the next available deadline 47 | wake_task(*task_id); 48 | rm_deadlines.pop(); 49 | continue; 50 | } 51 | } 52 | break; 53 | } 54 | if rm_deadlines.is_empty() { 55 | rtc.disable_event(RtcInterrupt::Compare0); 56 | } 57 | } 58 | 59 | enum TimerState { 60 | Init, 61 | Wait, 62 | } 63 | 64 | pub struct Timer { 65 | end_time: TickInstant, 66 | state: TimerState, 67 | } 68 | 69 | impl Timer { 70 | pub fn new(duration: TickDuration) -> Self { 71 | Self { 72 | end_time: Ticker::now() + duration, 73 | state: TimerState::Init, 74 | } 75 | } 76 | 77 | /// Registration places the deadline & its task_id onto a `BinaryHeap`, and 78 | /// then will attempt to schedule it (via COMPARE0) if it's earlier than 79 | /// the current deadline. 80 | fn register(&self, task_id: usize) { 81 | let new_deadline = self.end_time.ticks(); 82 | critical_section::with(|cs| { 83 | let mut rm_deadlines = WAKE_DEADLINES.borrow_ref_mut(cs); 84 | let is_earliest = if let Some((next_deadline, _)) = rm_deadlines.peek() { 85 | new_deadline < *next_deadline 86 | } else { 87 | true 88 | }; 89 | if rm_deadlines.push((new_deadline, task_id)).is_err() { 90 | // Dropping a deadline in this system can be Very Bad: 91 | // - In the LED task, the LED will stop updating, but may come 92 | // back to life on a button press... 93 | // - In a button task, it will never wake again 94 | // `panic` to raise awareness of the issue during development 95 | panic!("Deadline dropped for task {}!", task_id); 96 | } 97 | // schedule now if its the earliest 98 | if is_earliest { 99 | schedule_wakeup(rm_deadlines, TICKER.rtc.borrow_ref_mut(cs)); 100 | } 101 | }); 102 | } 103 | } 104 | 105 | impl Future for Timer { 106 | type Output = (); 107 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 108 | match self.state { 109 | TimerState::Init => { 110 | self.register(cx.waker().task_id()); 111 | self.state = TimerState::Wait; 112 | Poll::Pending 113 | } 114 | TimerState::Wait => { 115 | if Ticker::now() >= self.end_time { 116 | Poll::Ready(()) 117 | } else { 118 | Poll::Pending 119 | } 120 | } 121 | } 122 | } 123 | } 124 | 125 | pub async fn delay(duration: TickDuration) { 126 | Timer::new(duration).await; 127 | } 128 | 129 | static TICKER: Ticker = Ticker { 130 | ovf_count: AtomicU32::new(0), 131 | rtc: Mutex::new(RefCell::new(None)), 132 | }; 133 | 134 | /// Keeps track of time for the system using RTC0, which ticks away at a rate 135 | /// of 32,768/sec using a low-power oscillator that runs even when the core is 136 | /// powered down. 137 | pub struct Ticker { 138 | ovf_count: AtomicU32, 139 | rtc: Mutex>>>, 140 | } 141 | 142 | impl Ticker { 143 | /// Called on startup to get RTC0 going, then hoists the HAL representation 144 | /// of RTC0 into the `static TICKER`, where it can be accessed by the 145 | /// interrupt handler function or any `Timer` instance. 146 | pub fn init(rtc0: RTC0, nvic: &mut NVIC) { 147 | let mut rtc = Rtc::new(rtc0, 0).unwrap(); 148 | rtc.enable_counter(); 149 | #[cfg(feature = "trigger-overflow")] 150 | { 151 | rtc.trigger_overflow(); 152 | // wait for the counter to initialize with its close-to-overflow 153 | // value before going any further, otherwise one of the tasks could 154 | // schedule a wakeup that will get skipped over when init happens. 155 | while rtc.get_counter() == 0 {} 156 | } 157 | rtc.enable_event(RtcInterrupt::Overflow); 158 | rtc.enable_interrupt(RtcInterrupt::Overflow, Some(nvic)); 159 | rtc.enable_interrupt(RtcInterrupt::Compare0, Some(nvic)); 160 | critical_section::with(|cs| { 161 | TICKER.rtc.replace(cs, Some(rtc)); 162 | }); 163 | } 164 | 165 | /// Get the current time, which is a combination of: 166 | /// - The overflow count (upper 40 bits) 167 | /// - The counter value (lowest 24 bits) 168 | /// 169 | /// Extra care is needed to ensure the current overflow-count & counter 170 | /// value are collected during the same overflow-cycle. 171 | pub fn now() -> TickInstant { 172 | let ticks = { 173 | loop { 174 | let ovf_before = TICKER.ovf_count.load(Ordering::SeqCst); 175 | let counter = critical_section::with(|cs| { 176 | TICKER.rtc.borrow_ref(cs).as_ref().unwrap().get_counter() 177 | }); 178 | let ovf = TICKER.ovf_count.load(Ordering::SeqCst); 179 | if ovf_before == ovf { 180 | break ((ovf as u64) << 24 | counter as u64); 181 | } 182 | } 183 | }; 184 | TickInstant::from_ticks(ticks) 185 | } 186 | } 187 | 188 | #[interrupt] 189 | fn RTC0() { 190 | critical_section::with(|cs| { 191 | let mut rm_rtc = TICKER.rtc.borrow_ref_mut(cs); 192 | let rtc = rm_rtc.as_mut().unwrap(); 193 | if rtc.is_event_triggered(RtcInterrupt::Overflow) { 194 | rtc.reset_event(RtcInterrupt::Overflow); 195 | TICKER.ovf_count.fetch_add(1, Ordering::Relaxed); 196 | } 197 | if rtc.is_event_triggered(RtcInterrupt::Compare0) { 198 | rtc.reset_event(RtcInterrupt::Compare0); 199 | } 200 | 201 | // For OVF & COMPARE0 events, schedule the next wakeup. This should also 202 | // kill enough clock cycles to allow the event flags to clear. 203 | // (see nRF52833 Product Specification section 6.1.8) 204 | schedule_wakeup(WAKE_DEADLINES.borrow_ref_mut(cs), rm_rtc); 205 | }); 206 | } 207 | -------------------------------------------------------------------------------- /ch7_embassy/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /ch7_embassy/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | "rust-analyzer.cargo.target": "thumbv7em-none-eabihf" 4 | } -------------------------------------------------------------------------------- /ch7_embassy/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.3.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 10 | 11 | [[package]] 12 | name = "az" 13 | version = "1.2.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" 16 | 17 | [[package]] 18 | name = "bare-metal" 19 | version = "0.2.5" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 22 | dependencies = [ 23 | "rustc_version", 24 | ] 25 | 26 | [[package]] 27 | name = "bitfield" 28 | version = "0.13.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 31 | 32 | [[package]] 33 | name = "bytemuck" 34 | version = "1.16.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" 37 | 38 | [[package]] 39 | name = "byteorder" 40 | version = "1.5.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 43 | 44 | [[package]] 45 | name = "cfg-if" 46 | version = "1.0.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 49 | 50 | [[package]] 51 | name = "cortex-m" 52 | version = "0.7.7" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" 55 | dependencies = [ 56 | "bare-metal", 57 | "bitfield", 58 | "critical-section", 59 | "embedded-hal 0.2.7", 60 | "volatile-register", 61 | ] 62 | 63 | [[package]] 64 | name = "cortex-m-rt" 65 | version = "0.7.3" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" 68 | dependencies = [ 69 | "cortex-m-rt-macros", 70 | ] 71 | 72 | [[package]] 73 | name = "cortex-m-rt-macros" 74 | version = "0.7.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" 77 | dependencies = [ 78 | "proc-macro2", 79 | "quote", 80 | "syn 1.0.109", 81 | ] 82 | 83 | [[package]] 84 | name = "critical-section" 85 | version = "1.1.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 88 | 89 | [[package]] 90 | name = "crunchy" 91 | version = "0.2.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 94 | 95 | [[package]] 96 | name = "darling" 97 | version = "0.20.10" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 100 | dependencies = [ 101 | "darling_core", 102 | "darling_macro", 103 | ] 104 | 105 | [[package]] 106 | name = "darling_core" 107 | version = "0.20.10" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 110 | dependencies = [ 111 | "fnv", 112 | "ident_case", 113 | "proc-macro2", 114 | "quote", 115 | "strsim", 116 | "syn 2.0.72", 117 | ] 118 | 119 | [[package]] 120 | name = "darling_macro" 121 | version = "0.20.10" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 124 | dependencies = [ 125 | "darling_core", 126 | "quote", 127 | "syn 2.0.72", 128 | ] 129 | 130 | [[package]] 131 | name = "document-features" 132 | version = "0.2.10" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" 135 | dependencies = [ 136 | "litrs", 137 | ] 138 | 139 | [[package]] 140 | name = "embassy-embedded-hal" 141 | version = "0.1.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "eca4a9380d03e61063067b8239f67d2fa9f108ede7c46b4273804f6b79e59a1d" 144 | dependencies = [ 145 | "embassy-futures", 146 | "embassy-sync 0.5.0", 147 | "embassy-time", 148 | "embedded-hal 0.2.7", 149 | "embedded-hal 1.0.0", 150 | "embedded-hal-async", 151 | "embedded-storage", 152 | "embedded-storage-async", 153 | "nb 1.1.0", 154 | ] 155 | 156 | [[package]] 157 | name = "embassy-executor" 158 | version = "0.5.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "ec648daedd2143466eff4b3e8002024f9f6c1de4ab7666bb679688752624c925" 161 | dependencies = [ 162 | "cortex-m", 163 | "critical-section", 164 | "document-features", 165 | "embassy-executor-macros", 166 | "embassy-time-driver", 167 | "embassy-time-queue-driver", 168 | ] 169 | 170 | [[package]] 171 | name = "embassy-executor-macros" 172 | version = "0.4.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "ad454accf80050e9cf7a51e994132ba0e56286b31f9317b68703897c328c59b5" 175 | dependencies = [ 176 | "darling", 177 | "proc-macro2", 178 | "quote", 179 | "syn 2.0.72", 180 | ] 181 | 182 | [[package]] 183 | name = "embassy-futures" 184 | version = "0.1.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" 187 | 188 | [[package]] 189 | name = "embassy-hal-internal" 190 | version = "0.1.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "a0ec47cf8bab914018d4bd2b4f0aaeb46e4f52ab1e7985df88aeef2c6eda5aed" 193 | dependencies = [ 194 | "cortex-m", 195 | "critical-section", 196 | "num-traits", 197 | ] 198 | 199 | [[package]] 200 | name = "embassy-nrf" 201 | version = "0.1.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "2faba661a13ac3417714ef23aa191af65941586dee692dbffe76ff7d3529321b" 204 | dependencies = [ 205 | "cfg-if", 206 | "cortex-m", 207 | "cortex-m-rt", 208 | "critical-section", 209 | "document-features", 210 | "embassy-embedded-hal", 211 | "embassy-hal-internal", 212 | "embassy-sync 0.5.0", 213 | "embassy-time-driver", 214 | "embassy-usb-driver", 215 | "embedded-hal 0.2.7", 216 | "embedded-hal 1.0.0", 217 | "embedded-hal-async", 218 | "embedded-io", 219 | "embedded-io-async", 220 | "embedded-storage", 221 | "embedded-storage-async", 222 | "fixed", 223 | "nrf52805-pac", 224 | "nrf52810-pac", 225 | "nrf52811-pac", 226 | "nrf52820-pac", 227 | "nrf52832-pac", 228 | "nrf52833-pac", 229 | "nrf52840-pac", 230 | "nrf5340-app-pac", 231 | "nrf5340-net-pac", 232 | "nrf9160-pac", 233 | "rand_core", 234 | ] 235 | 236 | [[package]] 237 | name = "embassy-sync" 238 | version = "0.5.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" 241 | dependencies = [ 242 | "cfg-if", 243 | "critical-section", 244 | "embedded-io-async", 245 | "futures-util", 246 | "heapless", 247 | ] 248 | 249 | [[package]] 250 | name = "embassy-sync" 251 | version = "0.6.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "b3e0c49ff02ebe324faf3a8653ba91582e2d0a7fdef5bc88f449d5aa1bfcc05c" 254 | dependencies = [ 255 | "cfg-if", 256 | "critical-section", 257 | "embedded-io-async", 258 | "futures-util", 259 | "heapless", 260 | ] 261 | 262 | [[package]] 263 | name = "embassy-time" 264 | version = "0.3.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "274c019608a9004aed3cafc871e2a3c87ce9351d537dcaab4cc5db184d4a04b1" 267 | dependencies = [ 268 | "cfg-if", 269 | "critical-section", 270 | "document-features", 271 | "embassy-time-driver", 272 | "embassy-time-queue-driver", 273 | "embedded-hal 0.2.7", 274 | "embedded-hal 1.0.0", 275 | "embedded-hal-async", 276 | "futures-util", 277 | "heapless", 278 | ] 279 | 280 | [[package]] 281 | name = "embassy-time-driver" 282 | version = "0.1.0" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24" 285 | dependencies = [ 286 | "document-features", 287 | ] 288 | 289 | [[package]] 290 | name = "embassy-time-queue-driver" 291 | version = "0.1.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0" 294 | 295 | [[package]] 296 | name = "embassy-usb-driver" 297 | version = "0.1.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" 300 | 301 | [[package]] 302 | name = "embedded-hal" 303 | version = "0.2.7" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 306 | dependencies = [ 307 | "nb 0.1.3", 308 | "void", 309 | ] 310 | 311 | [[package]] 312 | name = "embedded-hal" 313 | version = "1.0.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 316 | 317 | [[package]] 318 | name = "embedded-hal-async" 319 | version = "1.0.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" 322 | dependencies = [ 323 | "embedded-hal 1.0.0", 324 | ] 325 | 326 | [[package]] 327 | name = "embedded-io" 328 | version = "0.6.1" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 331 | 332 | [[package]] 333 | name = "embedded-io-async" 334 | version = "0.6.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" 337 | dependencies = [ 338 | "embedded-io", 339 | ] 340 | 341 | [[package]] 342 | name = "embedded-storage" 343 | version = "0.3.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" 346 | 347 | [[package]] 348 | name = "embedded-storage-async" 349 | version = "0.4.1" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" 352 | dependencies = [ 353 | "embedded-storage", 354 | ] 355 | 356 | [[package]] 357 | name = "fixed" 358 | version = "1.28.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "85c6e0b89bf864acd20590dbdbad56f69aeb898abfc9443008fd7bd48b2cc85a" 361 | dependencies = [ 362 | "az", 363 | "bytemuck", 364 | "half", 365 | "typenum", 366 | ] 367 | 368 | [[package]] 369 | name = "fnv" 370 | version = "1.0.7" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 373 | 374 | [[package]] 375 | name = "fugit" 376 | version = "0.3.7" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 379 | dependencies = [ 380 | "gcd", 381 | ] 382 | 383 | [[package]] 384 | name = "futures" 385 | version = "0.3.30" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 388 | dependencies = [ 389 | "futures-channel", 390 | "futures-core", 391 | "futures-io", 392 | "futures-sink", 393 | "futures-task", 394 | "futures-util", 395 | ] 396 | 397 | [[package]] 398 | name = "futures-channel" 399 | version = "0.3.30" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 402 | dependencies = [ 403 | "futures-core", 404 | "futures-sink", 405 | ] 406 | 407 | [[package]] 408 | name = "futures-core" 409 | version = "0.3.30" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 412 | 413 | [[package]] 414 | name = "futures-io" 415 | version = "0.3.30" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 418 | 419 | [[package]] 420 | name = "futures-macro" 421 | version = "0.3.30" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 424 | dependencies = [ 425 | "proc-macro2", 426 | "quote", 427 | "syn 2.0.72", 428 | ] 429 | 430 | [[package]] 431 | name = "futures-sink" 432 | version = "0.3.30" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 435 | 436 | [[package]] 437 | name = "futures-task" 438 | version = "0.3.30" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 441 | 442 | [[package]] 443 | name = "futures-util" 444 | version = "0.3.30" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 447 | dependencies = [ 448 | "futures-core", 449 | "futures-macro", 450 | "futures-sink", 451 | "futures-task", 452 | "pin-project-lite", 453 | "pin-utils", 454 | ] 455 | 456 | [[package]] 457 | name = "gcd" 458 | version = "2.3.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 461 | 462 | [[package]] 463 | name = "half" 464 | version = "2.4.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 467 | dependencies = [ 468 | "cfg-if", 469 | "crunchy", 470 | ] 471 | 472 | [[package]] 473 | name = "hash32" 474 | version = "0.3.1" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 477 | dependencies = [ 478 | "byteorder", 479 | ] 480 | 481 | [[package]] 482 | name = "heapless" 483 | version = "0.8.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 486 | dependencies = [ 487 | "hash32", 488 | "portable-atomic", 489 | "stable_deref_trait", 490 | ] 491 | 492 | [[package]] 493 | name = "ident_case" 494 | version = "1.0.1" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 497 | 498 | [[package]] 499 | name = "litrs" 500 | version = "0.4.1" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" 503 | 504 | [[package]] 505 | name = "nb" 506 | version = "0.1.3" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 509 | dependencies = [ 510 | "nb 1.1.0", 511 | ] 512 | 513 | [[package]] 514 | name = "nb" 515 | version = "1.1.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 518 | 519 | [[package]] 520 | name = "nrf52805-pac" 521 | version = "0.12.2" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "a2da657648039d59f4de6bc31b948dd3a5d03b32529a4d5d19d9e2dd9d4bfa6c" 524 | dependencies = [ 525 | "cortex-m", 526 | "cortex-m-rt", 527 | "vcell", 528 | ] 529 | 530 | [[package]] 531 | name = "nrf52810-pac" 532 | version = "0.12.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "c26b12d5af17a9f4bb9a06ca9a1f814bca3d67bc8715b23f8dc230b09a227666" 535 | dependencies = [ 536 | "cortex-m", 537 | "cortex-m-rt", 538 | "vcell", 539 | ] 540 | 541 | [[package]] 542 | name = "nrf52811-pac" 543 | version = "0.12.2" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "4179b2a7ed0b2fd5e109d0fab9b4fc55b3936b2a4916a9306d22e5bc8dc1fd8f" 546 | dependencies = [ 547 | "cortex-m", 548 | "cortex-m-rt", 549 | "vcell", 550 | ] 551 | 552 | [[package]] 553 | name = "nrf52820-pac" 554 | version = "0.12.2" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "e4791cff995e6419a5ad1aebc3b3c9539d79125ca85eb5bfd2cff9b470b81071" 557 | dependencies = [ 558 | "cortex-m", 559 | "cortex-m-rt", 560 | "vcell", 561 | ] 562 | 563 | [[package]] 564 | name = "nrf52832-pac" 565 | version = "0.12.2" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "0242b685c9c15648fb803e155628f42ace457478b2cb930868f40cae2db925e0" 568 | dependencies = [ 569 | "cortex-m", 570 | "cortex-m-rt", 571 | "vcell", 572 | ] 573 | 574 | [[package]] 575 | name = "nrf52833-pac" 576 | version = "0.12.2" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "10e1358255b360cdc816dd7b6ef81be8c8499c0998277e5249bed222bd0f5241" 579 | dependencies = [ 580 | "cortex-m", 581 | "cortex-m-rt", 582 | "vcell", 583 | ] 584 | 585 | [[package]] 586 | name = "nrf52840-pac" 587 | version = "0.12.2" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "30713f36f1be02e5bc9abefa30eae4a1f943d810f199d4923d3ad062d1be1b3d" 590 | dependencies = [ 591 | "cortex-m", 592 | "cortex-m-rt", 593 | "vcell", 594 | ] 595 | 596 | [[package]] 597 | name = "nrf5340-app-pac" 598 | version = "0.12.2" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "7c88824573cd150fe9f27c1a48cea31a8cb24d3322df488875775143618c087a" 601 | dependencies = [ 602 | "cortex-m", 603 | "cortex-m-rt", 604 | "vcell", 605 | ] 606 | 607 | [[package]] 608 | name = "nrf5340-net-pac" 609 | version = "0.12.2" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "e5c03e44df22fe5888109fe42e523162c7059adf4d30860f4f73ecc8b1fc16fe" 612 | dependencies = [ 613 | "cortex-m", 614 | "cortex-m-rt", 615 | "vcell", 616 | ] 617 | 618 | [[package]] 619 | name = "nrf9160-pac" 620 | version = "0.12.2" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "7344d74afb5684e00c48d175cad9619f36d629cfb0687d33b4d1bb86fba688f4" 623 | dependencies = [ 624 | "cortex-m", 625 | "cortex-m-rt", 626 | "vcell", 627 | ] 628 | 629 | [[package]] 630 | name = "num-traits" 631 | version = "0.2.19" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 634 | dependencies = [ 635 | "autocfg", 636 | ] 637 | 638 | [[package]] 639 | name = "panic-rtt-target" 640 | version = "0.1.3" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "608d1d809dd8960d5e8364981279c7ab280a13d98b99eae049885a7ab2b1cbfe" 643 | dependencies = [ 644 | "critical-section", 645 | "rtt-target", 646 | ] 647 | 648 | [[package]] 649 | name = "pin-project-lite" 650 | version = "0.2.14" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 653 | 654 | [[package]] 655 | name = "pin-utils" 656 | version = "0.1.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 659 | 660 | [[package]] 661 | name = "portable-atomic" 662 | version = "1.7.0" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 665 | 666 | [[package]] 667 | name = "proc-macro2" 668 | version = "1.0.86" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 671 | dependencies = [ 672 | "unicode-ident", 673 | ] 674 | 675 | [[package]] 676 | name = "quote" 677 | version = "1.0.36" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 680 | dependencies = [ 681 | "proc-macro2", 682 | ] 683 | 684 | [[package]] 685 | name = "rand_core" 686 | version = "0.6.4" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 689 | 690 | [[package]] 691 | name = "rtt-target" 692 | version = "0.5.0" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "10b34c9e6832388e45f3c01f1bb60a016384a0a4ad80cdd7d34913bed25037f0" 695 | dependencies = [ 696 | "critical-section", 697 | "ufmt-write", 698 | ] 699 | 700 | [[package]] 701 | name = "rustc_version" 702 | version = "0.2.3" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 705 | dependencies = [ 706 | "semver", 707 | ] 708 | 709 | [[package]] 710 | name = "semver" 711 | version = "0.9.0" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 714 | dependencies = [ 715 | "semver-parser", 716 | ] 717 | 718 | [[package]] 719 | name = "semver-parser" 720 | version = "0.7.0" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 723 | 724 | [[package]] 725 | name = "stable_deref_trait" 726 | version = "1.2.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 729 | 730 | [[package]] 731 | name = "strsim" 732 | version = "0.11.1" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 735 | 736 | [[package]] 737 | name = "syn" 738 | version = "1.0.109" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 741 | dependencies = [ 742 | "proc-macro2", 743 | "quote", 744 | "unicode-ident", 745 | ] 746 | 747 | [[package]] 748 | name = "syn" 749 | version = "2.0.72" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" 752 | dependencies = [ 753 | "proc-macro2", 754 | "quote", 755 | "unicode-ident", 756 | ] 757 | 758 | [[package]] 759 | name = "typenum" 760 | version = "1.17.0" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 763 | 764 | [[package]] 765 | name = "ufmt-write" 766 | version = "0.1.0" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 769 | 770 | [[package]] 771 | name = "unicode-ident" 772 | version = "1.0.12" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 775 | 776 | [[package]] 777 | name = "vcell" 778 | version = "0.1.3" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 781 | 782 | [[package]] 783 | name = "void" 784 | version = "1.0.2" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 787 | 788 | [[package]] 789 | name = "volatile-register" 790 | version = "0.2.2" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" 793 | dependencies = [ 794 | "vcell", 795 | ] 796 | 797 | [[package]] 798 | name = "zero-to-async" 799 | version = "0.1.0" 800 | dependencies = [ 801 | "cortex-m", 802 | "cortex-m-rt", 803 | "critical-section", 804 | "embassy-executor", 805 | "embassy-nrf", 806 | "embassy-sync 0.6.0", 807 | "embassy-time", 808 | "embedded-hal 1.0.0", 809 | "fugit", 810 | "futures", 811 | "heapless", 812 | "panic-rtt-target", 813 | "rtt-target", 814 | ] 815 | -------------------------------------------------------------------------------- /ch7_embassy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zero-to-async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 8 | cortex-m-rt = "0.7.3" 9 | critical-section = "1.1.2" 10 | embassy-executor = { version = "0.6.0", features = [ 11 | "arch-cortex-m", 12 | "executor-thread", 13 | "integrated-timers", 14 | ] } 15 | embassy-nrf = { version = "0.2.0", features = [ 16 | "nrf52833", 17 | "time-driver-rtc1", 18 | "gpiote", 19 | ] } 20 | embassy-sync = "0.6.0" 21 | embassy-time = "0.3.2" 22 | embedded-hal = "1.0.0" 23 | fugit = "0.3.7" 24 | futures = { version = "0.3.30", default-features = false, features = [ 25 | "async-await", 26 | ] } 27 | heapless = { version = "0.8.0", features = ["portable-atomic"] } 28 | panic-rtt-target = "0.1.3" 29 | rtt-target = "0.5.0" 30 | 31 | [features] 32 | trigger-overflow = [] 33 | -------------------------------------------------------------------------------- /ch7_embassy/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nRF52833_xxAA" 3 | 4 | [default.reset] 5 | halt_afterwards = false 6 | 7 | [default.gdb] 8 | enabled = false 9 | 10 | [default.rtt] 11 | enabled = true 12 | -------------------------------------------------------------------------------- /ch7_embassy/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x00000000, LENGTH = 512K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 6 | } 7 | -------------------------------------------------------------------------------- /ch7_embassy/src/button.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub enum ButtonDirection { 3 | Left, 4 | Right, 5 | } 6 | -------------------------------------------------------------------------------- /ch7_embassy/src/led.rs: -------------------------------------------------------------------------------- 1 | use embassy_nrf::gpio::Output; 2 | use rtt_target::rprintln; 3 | 4 | use crate::button::ButtonDirection; 5 | 6 | const NUM_COLS: usize = 5; 7 | 8 | pub struct LedRow { 9 | col: [Output<'static>; NUM_COLS], 10 | active_col: usize, 11 | } 12 | 13 | impl LedRow { 14 | pub fn new( 15 | col: [Output<'static>; NUM_COLS], 16 | ) -> Self { 17 | Self { 18 | col, 19 | active_col: 0, 20 | } 21 | } 22 | 23 | pub fn shift(&mut self, direction: ButtonDirection) { 24 | rprintln!("Button press detected.."); 25 | // switch off current/old LED 26 | self.col[self.active_col].set_high(); 27 | self.active_col = match direction { 28 | ButtonDirection::Left => match self.active_col { 29 | 0 => NUM_COLS - 1, 30 | _ => self.active_col - 1, 31 | } 32 | ButtonDirection::Right => (self.active_col + 1) % NUM_COLS, 33 | }; 34 | // switch off new LED: moving to Toggle will then switch it on 35 | self.col[self.active_col].set_high(); 36 | } 37 | 38 | pub fn toggle(&mut self) { 39 | rprintln!("Blinking LED {}", self.active_col); 40 | #[cfg(feature = "trigger-overflow")] 41 | { 42 | use crate::time::Ticker; 43 | let time = Ticker::now(); 44 | rprintln!( 45 | "Time: 0x{:x} ticks, {} ms", 46 | time.ticks(), 47 | time.duration_since_epoch().to_millis(), 48 | ); 49 | } 50 | self.col[self.active_col].toggle(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ch7_embassy/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod button; 5 | mod led; 6 | 7 | use button::ButtonDirection; 8 | use embassy_executor::Spawner; 9 | use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; 10 | use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel}; 11 | use embassy_time::Timer; 12 | use futures::{select_biased, FutureExt}; 13 | use led::LedRow; 14 | use panic_rtt_target as _; 15 | use rtt_target::rtt_init_print; 16 | 17 | static CHANNEL: Channel = Channel::new(); 18 | 19 | #[embassy_executor::main] 20 | async fn main(spawner: Spawner) { 21 | rtt_init_print!(); 22 | let p = embassy_nrf::init(Default::default()); 23 | 24 | spawner 25 | .spawn(button_task(p.P0_14.degrade(), ButtonDirection::Left)) 26 | .unwrap(); 27 | spawner 28 | .spawn(button_task(p.P0_23.degrade(), ButtonDirection::Right)) 29 | .unwrap(); 30 | 31 | let _row1 = led_pin(p.P0_21.degrade()); 32 | let col = [ 33 | led_pin(p.P0_28.degrade()), 34 | led_pin(p.P0_11.degrade()), 35 | led_pin(p.P0_31.degrade()), 36 | led_pin(p.P1_05.degrade()), 37 | led_pin(p.P0_30.degrade()), 38 | ]; 39 | 40 | // LED task: 41 | let mut blinker = LedRow::new(col); 42 | loop { 43 | blinker.toggle(); 44 | select_biased! { 45 | direction = CHANNEL.receive().fuse() => { 46 | blinker.shift(direction); 47 | } 48 | _ = Timer::after_millis(500).fuse() => {} 49 | } 50 | } 51 | } 52 | 53 | fn led_pin(pin: AnyPin) -> Output<'static> { 54 | Output::new(pin, Level::High, OutputDrive::Standard) 55 | } 56 | 57 | #[embassy_executor::task(pool_size = 2)] 58 | async fn button_task( 59 | pin: AnyPin, 60 | direction: ButtonDirection, 61 | ) { 62 | let mut input = Input::new(pin, Pull::None); 63 | loop { 64 | input.wait_for_low().await; 65 | CHANNEL.send(direction).await; 66 | Timer::after_millis(100).await; 67 | input.wait_for_high().await; 68 | } 69 | } 70 | --------------------------------------------------------------------------------