First commit
This commit is contained in:
		
						commit
						9a57deef9b
					
				
							
								
								
									
										27
									
								
								.cargo/config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.cargo/config
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||||||
|  | runner = "probe-run --chip nRF52840_xxAA --defmt" | ||||||
|  | 
 | ||||||
|  | rustflags = [ | ||||||
|  |   # LLD (shipped with the Rust toolchain) is used as the default linker | ||||||
|  |   "-C", "link-arg=--nmagic", | ||||||
|  |   "-C", "link-arg=-Tlink.x", | ||||||
|  |   "-C", "link-arg=-Tdefmt.x", | ||||||
|  | 
 | ||||||
|  |   # if you run into problems with LLD switch to the GNU linker by commenting out | ||||||
|  |   # this line | ||||||
|  |   # "-C", "linker=arm-none-eabi-ld", | ||||||
|  | 
 | ||||||
|  |   # if you need to link to pre-compiled C libraries provided by a C toolchain | ||||||
|  |   # use GCC as the linker by commenting out both lines above and then | ||||||
|  |   # uncommenting the three lines below | ||||||
|  |   # "-C", "linker=arm-none-eabi-gcc", | ||||||
|  |   # "-C", "link-arg=-Wl,-Tlink.x", | ||||||
|  |   # "-C", "link-arg=-nostartfiles", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [build] | ||||||
|  | # Pick ONE of these compilation targets | ||||||
|  | # target = "thumbv6m-none-eabi"    # Cortex-M0 and Cortex-M0+ | ||||||
|  | # target = "thumbv7m-none-eabi"    # Cortex-M3 | ||||||
|  | # target = "thumbv7em-none-eabi"   # Cortex-M4 and Cortex-M7 (no FPU) | ||||||
|  | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | ||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | /target | ||||||
|  | Cargo.lock | ||||||
							
								
								
									
										12
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | { | ||||||
|  |   "editor.formatOnSave": true, | ||||||
|  |   "rust-analyzer.cargo.allFeatures": false, | ||||||
|  |   "rust-analyzer.checkOnSave.allFeatures": false, | ||||||
|  |   "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", | ||||||
|  |   "rust-analyzer.checkOnSave.allTargets": false, | ||||||
|  |   "files.watcherExclude": { | ||||||
|  |     "**/.git/objects/**": true, | ||||||
|  |     "**/.git/subtree-cache/**": true, | ||||||
|  |     "**/target/**": true | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | 
 | ||||||
|  | [workspace] | ||||||
|  | members = [ | ||||||
|  |     "embassy", | ||||||
|  |     "embassy-nrf", | ||||||
|  |     "examples", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [patch.crates-io] | ||||||
|  | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } | ||||||
|  | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | ||||||
|  | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | ||||||
|  | static-executor = { git = "https://github.com/Dirbaio/static-executor" } | ||||||
|  | static-executor-cortex-m = { git = "https://github.com/Dirbaio/static-executor" } | ||||||
|  | 
 | ||||||
|  | [profile.dev] | ||||||
|  | codegen-units = 1 | ||||||
|  | debug = 2 | ||||||
|  | debug-assertions = true | ||||||
|  | incremental = false | ||||||
|  | opt-level = 3 | ||||||
|  | overflow-checks = true | ||||||
|  | 
 | ||||||
|  | [profile.release] | ||||||
|  | codegen-units = 1 | ||||||
|  | debug = 2 | ||||||
|  | debug-assertions = false | ||||||
|  | incremental = false | ||||||
|  | lto = "fat" | ||||||
|  | opt-level = 3 | ||||||
|  | overflow-checks = false | ||||||
|  | 
 | ||||||
|  | # do not optimize proc-macro crates = faster builds from scratch | ||||||
|  | [profile.dev.build-override] | ||||||
|  | codegen-units = 8 | ||||||
|  | debug = false | ||||||
|  | debug-assertions = false | ||||||
|  | opt-level = 0 | ||||||
|  | overflow-checks = false | ||||||
|  | 
 | ||||||
|  | [profile.release.build-override] | ||||||
|  | codegen-units = 8 | ||||||
|  | debug = false | ||||||
|  | debug-assertions = false | ||||||
|  | opt-level = 0 | ||||||
|  | overflow-checks = false | ||||||
							
								
								
									
										201
									
								
								LICENSE-APACHE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								LICENSE-APACHE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,201 @@ | |||||||
|  |                               Apache License | ||||||
|  |                         Version 2.0, January 2004 | ||||||
|  |                      http://www.apache.org/licenses/ | ||||||
|  | 
 | ||||||
|  | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||||
|  | 
 | ||||||
|  | 1. Definitions. | ||||||
|  | 
 | ||||||
|  |    "License" shall mean the terms and conditions for use, reproduction, | ||||||
|  |    and distribution as defined by Sections 1 through 9 of this document. | ||||||
|  | 
 | ||||||
|  |    "Licensor" shall mean the copyright owner or entity authorized by | ||||||
|  |    the copyright owner that is granting the License. | ||||||
|  | 
 | ||||||
|  |    "Legal Entity" shall mean the union of the acting entity and all | ||||||
|  |    other entities that control, are controlled by, or are under common | ||||||
|  |    control with that entity. For the purposes of this definition, | ||||||
|  |    "control" means (i) the power, direct or indirect, to cause the | ||||||
|  |    direction or management of such entity, whether by contract or | ||||||
|  |    otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||||
|  |    outstanding shares, or (iii) beneficial ownership of such entity. | ||||||
|  | 
 | ||||||
|  |    "You" (or "Your") shall mean an individual or Legal Entity | ||||||
|  |    exercising permissions granted by this License. | ||||||
|  | 
 | ||||||
|  |    "Source" form shall mean the preferred form for making modifications, | ||||||
|  |    including but not limited to software source code, documentation | ||||||
|  |    source, and configuration files. | ||||||
|  | 
 | ||||||
|  |    "Object" form shall mean any form resulting from mechanical | ||||||
|  |    transformation or translation of a Source form, including but | ||||||
|  |    not limited to compiled object code, generated documentation, | ||||||
|  |    and conversions to other media types. | ||||||
|  | 
 | ||||||
|  |    "Work" shall mean the work of authorship, whether in Source or | ||||||
|  |    Object form, made available under the License, as indicated by a | ||||||
|  |    copyright notice that is included in or attached to the work | ||||||
|  |    (an example is provided in the Appendix below). | ||||||
|  | 
 | ||||||
|  |    "Derivative Works" shall mean any work, whether in Source or Object | ||||||
|  |    form, that is based on (or derived from) the Work and for which the | ||||||
|  |    editorial revisions, annotations, elaborations, or other modifications | ||||||
|  |    represent, as a whole, an original work of authorship. For the purposes | ||||||
|  |    of this License, Derivative Works shall not include works that remain | ||||||
|  |    separable from, or merely link (or bind by name) to the interfaces of, | ||||||
|  |    the Work and Derivative Works thereof. | ||||||
|  | 
 | ||||||
|  |    "Contribution" shall mean any work of authorship, including | ||||||
|  |    the original version of the Work and any modifications or additions | ||||||
|  |    to that Work or Derivative Works thereof, that is intentionally | ||||||
|  |    submitted to Licensor for inclusion in the Work by the copyright owner | ||||||
|  |    or by an individual or Legal Entity authorized to submit on behalf of | ||||||
|  |    the copyright owner. For the purposes of this definition, "submitted" | ||||||
|  |    means any form of electronic, verbal, or written communication sent | ||||||
|  |    to the Licensor or its representatives, including but not limited to | ||||||
|  |    communication on electronic mailing lists, source code control systems, | ||||||
|  |    and issue tracking systems that are managed by, or on behalf of, the | ||||||
|  |    Licensor for the purpose of discussing and improving the Work, but | ||||||
|  |    excluding communication that is conspicuously marked or otherwise | ||||||
|  |    designated in writing by the copyright owner as "Not a Contribution." | ||||||
|  | 
 | ||||||
|  |    "Contributor" shall mean Licensor and any individual or Legal Entity | ||||||
|  |    on behalf of whom a Contribution has been received by Licensor and | ||||||
|  |    subsequently incorporated within the Work. | ||||||
|  | 
 | ||||||
|  | 2. Grant of Copyright License. Subject to the terms and conditions of | ||||||
|  |    this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |    copyright license to reproduce, prepare Derivative Works of, | ||||||
|  |    publicly display, publicly perform, sublicense, and distribute the | ||||||
|  |    Work and such Derivative Works in Source or Object form. | ||||||
|  | 
 | ||||||
|  | 3. Grant of Patent License. Subject to the terms and conditions of | ||||||
|  |    this License, each Contributor hereby grants to You a perpetual, | ||||||
|  |    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||||
|  |    (except as stated in this section) patent license to make, have made, | ||||||
|  |    use, offer to sell, sell, import, and otherwise transfer the Work, | ||||||
|  |    where such license applies only to those patent claims licensable | ||||||
|  |    by such Contributor that are necessarily infringed by their | ||||||
|  |    Contribution(s) alone or by combination of their Contribution(s) | ||||||
|  |    with the Work to which such Contribution(s) was submitted. If You | ||||||
|  |    institute patent litigation against any entity (including a | ||||||
|  |    cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||||
|  |    or a Contribution incorporated within the Work constitutes direct | ||||||
|  |    or contributory patent infringement, then any patent licenses | ||||||
|  |    granted to You under this License for that Work shall terminate | ||||||
|  |    as of the date such litigation is filed. | ||||||
|  | 
 | ||||||
|  | 4. Redistribution. You may reproduce and distribute copies of the | ||||||
|  |    Work or Derivative Works thereof in any medium, with or without | ||||||
|  |    modifications, and in Source or Object form, provided that You | ||||||
|  |    meet the following conditions: | ||||||
|  | 
 | ||||||
|  |    (a) You must give any other recipients of the Work or | ||||||
|  |        Derivative Works a copy of this License; and | ||||||
|  | 
 | ||||||
|  |    (b) You must cause any modified files to carry prominent notices | ||||||
|  |        stating that You changed the files; and | ||||||
|  | 
 | ||||||
|  |    (c) You must retain, in the Source form of any Derivative Works | ||||||
|  |        that You distribute, all copyright, patent, trademark, and | ||||||
|  |        attribution notices from the Source form of the Work, | ||||||
|  |        excluding those notices that do not pertain to any part of | ||||||
|  |        the Derivative Works; and | ||||||
|  | 
 | ||||||
|  |    (d) If the Work includes a "NOTICE" text file as part of its | ||||||
|  |        distribution, then any Derivative Works that You distribute must | ||||||
|  |        include a readable copy of the attribution notices contained | ||||||
|  |        within such NOTICE file, excluding those notices that do not | ||||||
|  |        pertain to any part of the Derivative Works, in at least one | ||||||
|  |        of the following places: within a NOTICE text file distributed | ||||||
|  |        as part of the Derivative Works; within the Source form or | ||||||
|  |        documentation, if provided along with the Derivative Works; or, | ||||||
|  |        within a display generated by the Derivative Works, if and | ||||||
|  |        wherever such third-party notices normally appear. The contents | ||||||
|  |        of the NOTICE file are for informational purposes only and | ||||||
|  |        do not modify the License. You may add Your own attribution | ||||||
|  |        notices within Derivative Works that You distribute, alongside | ||||||
|  |        or as an addendum to the NOTICE text from the Work, provided | ||||||
|  |        that such additional attribution notices cannot be construed | ||||||
|  |        as modifying the License. | ||||||
|  | 
 | ||||||
|  |    You may add Your own copyright statement to Your modifications and | ||||||
|  |    may provide additional or different license terms and conditions | ||||||
|  |    for use, reproduction, or distribution of Your modifications, or | ||||||
|  |    for any such Derivative Works as a whole, provided Your use, | ||||||
|  |    reproduction, and distribution of the Work otherwise complies with | ||||||
|  |    the conditions stated in this License. | ||||||
|  | 
 | ||||||
|  | 5. Submission of Contributions. Unless You explicitly state otherwise, | ||||||
|  |    any Contribution intentionally submitted for inclusion in the Work | ||||||
|  |    by You to the Licensor shall be under the terms and conditions of | ||||||
|  |    this License, without any additional terms or conditions. | ||||||
|  |    Notwithstanding the above, nothing herein shall supersede or modify | ||||||
|  |    the terms of any separate license agreement you may have executed | ||||||
|  |    with Licensor regarding such Contributions. | ||||||
|  | 
 | ||||||
|  | 6. Trademarks. This License does not grant permission to use the trade | ||||||
|  |    names, trademarks, service marks, or product names of the Licensor, | ||||||
|  |    except as required for reasonable and customary use in describing the | ||||||
|  |    origin of the Work and reproducing the content of the NOTICE file. | ||||||
|  | 
 | ||||||
|  | 7. Disclaimer of Warranty. Unless required by applicable law or | ||||||
|  |    agreed to in writing, Licensor provides the Work (and each | ||||||
|  |    Contributor provides its Contributions) on an "AS IS" BASIS, | ||||||
|  |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||||
|  |    implied, including, without limitation, any warranties or conditions | ||||||
|  |    of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||||
|  |    PARTICULAR PURPOSE. You are solely responsible for determining the | ||||||
|  |    appropriateness of using or redistributing the Work and assume any | ||||||
|  |    risks associated with Your exercise of permissions under this License. | ||||||
|  | 
 | ||||||
|  | 8. Limitation of Liability. In no event and under no legal theory, | ||||||
|  |    whether in tort (including negligence), contract, or otherwise, | ||||||
|  |    unless required by applicable law (such as deliberate and grossly | ||||||
|  |    negligent acts) or agreed to in writing, shall any Contributor be | ||||||
|  |    liable to You for damages, including any direct, indirect, special, | ||||||
|  |    incidental, or consequential damages of any character arising as a | ||||||
|  |    result of this License or out of the use or inability to use the | ||||||
|  |    Work (including but not limited to damages for loss of goodwill, | ||||||
|  |    work stoppage, computer failure or malfunction, or any and all | ||||||
|  |    other commercial damages or losses), even if such Contributor | ||||||
|  |    has been advised of the possibility of such damages. | ||||||
|  | 
 | ||||||
|  | 9. Accepting Warranty or Additional Liability. While redistributing | ||||||
|  |    the Work or Derivative Works thereof, You may choose to offer, | ||||||
|  |    and charge a fee for, acceptance of support, warranty, indemnity, | ||||||
|  |    or other liability obligations and/or rights consistent with this | ||||||
|  |    License. However, in accepting such obligations, You may act only | ||||||
|  |    on Your own behalf and on Your sole responsibility, not on behalf | ||||||
|  |    of any other Contributor, and only if You agree to indemnify, | ||||||
|  |    defend, and hold each Contributor harmless for any liability | ||||||
|  |    incurred by, or claims asserted against, such Contributor by reason | ||||||
|  |    of your accepting any such warranty or additional liability. | ||||||
|  | 
 | ||||||
|  | END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  | APPENDIX: How to apply the Apache License to your work. | ||||||
|  | 
 | ||||||
|  |    To apply the Apache License to your work, attach the following | ||||||
|  |    boilerplate notice, with the fields enclosed by brackets "[]" | ||||||
|  |    replaced with your own identifying information. (Don't include | ||||||
|  |    the brackets!)  The text should be enclosed in the appropriate | ||||||
|  |    comment syntax for the file format. We also recommend that a | ||||||
|  |    file or class name and description of purpose be included on the | ||||||
|  |    same "printed page" as the copyright notice for easier | ||||||
|  |    identification within third-party archives. | ||||||
|  | 
 | ||||||
|  | Copyright [yyyy] [name of copyright owner] | ||||||
|  | 
 | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  | 
 | ||||||
|  | 	http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | 
 | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
							
								
								
									
										25
									
								
								LICENSE-MIT
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								LICENSE-MIT
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | Copyright (c) 2020 Dario Nieuwenhuis | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any | ||||||
|  | person obtaining a copy of this software and associated | ||||||
|  | documentation files (the "Software"), to deal in the | ||||||
|  | Software without restriction, including without | ||||||
|  | limitation the rights to use, copy, modify, merge, | ||||||
|  | publish, distribute, sublicense, and/or sell copies of | ||||||
|  | the Software, and to permit persons to whom the Software | ||||||
|  | is furnished to do so, subject to the following | ||||||
|  | conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice | ||||||
|  | shall be included in all copies or substantial portions | ||||||
|  | of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF | ||||||
|  | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED | ||||||
|  | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | ||||||
|  | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | ||||||
|  | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||||||
|  | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR | ||||||
|  | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||||
|  | DEALINGS IN THE SOFTWARE. | ||||||
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | # Embassy | ||||||
|  | 
 | ||||||
|  | Embassy is a project to make async/await a first-class option for embedded development. | ||||||
|  | 
 | ||||||
|  | The `embassy` crate defines some traits. | ||||||
|  | 
 | ||||||
|  | - `embassy::io`: Traits for byte-stream IO, essentially `no_std` compatible versions of `futures::io`. | ||||||
|  | - `embassy::flash`: Trait for an async flash device. | ||||||
|  | - More traits for SPI, I2C, UART async HAL coming soon. | ||||||
|  | 
 | ||||||
|  | The `embassy-nrf` crate contains implementations for nRF 52 series SoCs. | ||||||
|  | 
 | ||||||
|  | - `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`. | ||||||
|  | - `qspi`: QSPI driver implementing `Flash`. | ||||||
|  | 
 | ||||||
|  | Currently Embassy requires a recent nightly, mainly for `generic_associated_types` (for trait funcs returning futures) and `type_alias_impl_trait` (for returning futures implemented with `async{}` blocks). Stable support is a non-goal. | ||||||
|  | 
 | ||||||
|  | ## Why the name? | ||||||
|  | 
 | ||||||
|  | EMBedded ASYnc. | ||||||
|  | 
 | ||||||
|  | ## License | ||||||
|  | 
 | ||||||
|  | This work is licensed under either of | ||||||
|  | 
 | ||||||
|  | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or | ||||||
|  |   http://www.apache.org/licenses/LICENSE-2.0) | ||||||
|  | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) | ||||||
|  | 
 | ||||||
|  | at your option. | ||||||
							
								
								
									
										38
									
								
								embassy-nrf/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								embassy-nrf/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | [package] | ||||||
|  | name = "embassy-nrf" | ||||||
|  | version = "0.1.0" | ||||||
|  | authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"] | ||||||
|  | edition = "2018" | ||||||
|  | 
 | ||||||
|  | [features] | ||||||
|  | default = [ | ||||||
|  |     "defmt-default", | ||||||
|  | ] | ||||||
|  | defmt-default = [] | ||||||
|  | defmt-trace = [] | ||||||
|  | defmt-debug = [] | ||||||
|  | defmt-info = [] | ||||||
|  | defmt-warn = [] | ||||||
|  | defmt-error = [] | ||||||
|  | 
 | ||||||
|  | nrf52810 = ["nrf52810-pac"] | ||||||
|  | nrf52811 = ["nrf52811-pac"] | ||||||
|  | nrf52832 = ["nrf52832-pac"] | ||||||
|  | nrf52833 = ["nrf52833-pac"] | ||||||
|  | nrf52840 = ["nrf52840-pac"] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | embassy = { version = "0.1.0", path = "../embassy" } | ||||||
|  | cortex-m-rt = "0.6.12" | ||||||
|  | cortex-m        = { version = "0.6.3" } | ||||||
|  | embedded-hal    = { version = "0.2.4" } | ||||||
|  | nrf52840-hal    = { version = "0.11.0" } | ||||||
|  | bare-metal = { version = "0.2.0", features = ["const-fn"] } | ||||||
|  | defmt = "0.1.0" | ||||||
|  | 
 | ||||||
|  | nrf52810-pac  = { version = "0.9.0", optional = true } | ||||||
|  | nrf52811-pac  = { version = "0.9.0", optional = true } | ||||||
|  | nrf52832-pac  = { version = "0.9.0", optional = true } | ||||||
|  | nrf52833-pac  = { version = "0.9.0", optional = true } | ||||||
|  | nrf52840-pac  = { version = "0.9.0", optional = true } | ||||||
							
								
								
									
										131
									
								
								embassy-nrf/src/interrupt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								embassy-nrf/src/interrupt.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | //! Interrupt management
 | ||||||
|  | //!
 | ||||||
|  | //! This module implements an API for managing interrupts compatible with
 | ||||||
|  | //! nrf_softdevice::interrupt. Intended for switching between the two at compile-time.
 | ||||||
|  | 
 | ||||||
|  | use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; | ||||||
|  | 
 | ||||||
|  | use crate::pac::{NVIC, NVIC_PRIO_BITS}; | ||||||
|  | 
 | ||||||
|  | // Re-exports
 | ||||||
|  | pub use crate::pac::Interrupt; | ||||||
|  | pub use crate::pac::Interrupt::*; // needed for cortex-m-rt #[interrupt]
 | ||||||
|  | pub use bare_metal::{CriticalSection, Mutex}; | ||||||
|  | 
 | ||||||
|  | #[derive(defmt::Format, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | ||||||
|  | #[repr(u8)] | ||||||
|  | pub enum Priority { | ||||||
|  |     Level0 = 0, | ||||||
|  |     Level1 = 1, | ||||||
|  |     Level2 = 2, | ||||||
|  |     Level3 = 3, | ||||||
|  |     Level4 = 4, | ||||||
|  |     Level5 = 5, | ||||||
|  |     Level6 = 6, | ||||||
|  |     Level7 = 7, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Priority { | ||||||
|  |     #[inline] | ||||||
|  |     fn to_nvic(self) -> u8 { | ||||||
|  |         (self as u8) << (8 - NVIC_PRIO_BITS) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[inline] | ||||||
|  |     fn from_nvic(priority: u8) -> Self { | ||||||
|  |         match priority >> (8 - NVIC_PRIO_BITS) { | ||||||
|  |             0 => Self::Level0, | ||||||
|  |             1 => Self::Level1, | ||||||
|  |             2 => Self::Level2, | ||||||
|  |             3 => Self::Level3, | ||||||
|  |             4 => Self::Level4, | ||||||
|  |             5 => Self::Level5, | ||||||
|  |             6 => Self::Level6, | ||||||
|  |             7 => Self::Level7, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static CS_FLAG: AtomicBool = AtomicBool::new(false); | ||||||
|  | static mut CS_MASK: [u32; 2] = [0; 2]; | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn free<F, R>(f: F) -> R | ||||||
|  | where | ||||||
|  |     F: FnOnce(&CriticalSection) -> R, | ||||||
|  | { | ||||||
|  |     unsafe { | ||||||
|  |         // TODO: assert that we're in privileged level
 | ||||||
|  |         // Needed because disabling irqs in non-privileged level is a noop, which would break safety.
 | ||||||
|  | 
 | ||||||
|  |         let primask: u32; | ||||||
|  |         asm!("mrs {}, PRIMASK", out(reg) primask); | ||||||
|  | 
 | ||||||
|  |         asm!("cpsid i"); | ||||||
|  | 
 | ||||||
|  |         // Prevent compiler from reordering operations inside/outside the critical section.
 | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  | 
 | ||||||
|  |         let r = f(&CriticalSection::new()); | ||||||
|  | 
 | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  | 
 | ||||||
|  |         if primask & 1 == 0 { | ||||||
|  |             asm!("cpsie i"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         r | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn enable(irq: Interrupt) { | ||||||
|  |     unsafe { | ||||||
|  |         NVIC::unmask(irq); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn disable(irq: Interrupt) { | ||||||
|  |     NVIC::mask(irq); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn is_active(irq: Interrupt) -> bool { | ||||||
|  |     NVIC::is_active(irq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn is_enabled(irq: Interrupt) -> bool { | ||||||
|  |     NVIC::is_enabled(irq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn is_pending(irq: Interrupt) -> bool { | ||||||
|  |     NVIC::is_pending(irq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn pend(irq: Interrupt) { | ||||||
|  |     NVIC::pend(irq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn unpend(irq: Interrupt) { | ||||||
|  |     NVIC::unpend(irq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn get_priority(irq: Interrupt) -> Priority { | ||||||
|  |     Priority::from_nvic(NVIC::get_priority(irq)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[inline] | ||||||
|  | pub fn set_priority(irq: Interrupt, prio: Priority) { | ||||||
|  |     unsafe { | ||||||
|  |         cortex_m::peripheral::Peripherals::steal() | ||||||
|  |             .NVIC | ||||||
|  |             .set_priority(irq, prio.to_nvic()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								embassy-nrf/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								embassy-nrf/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![feature(generic_associated_types)] | ||||||
|  | #![feature(asm)] | ||||||
|  | #![feature(type_alias_impl_trait)] | ||||||
|  | 
 | ||||||
|  | #[cfg(not(any(
 | ||||||
|  |     feature = "nrf52810", | ||||||
|  |     feature = "nrf52811", | ||||||
|  |     feature = "nrf52832", | ||||||
|  |     feature = "nrf52833", | ||||||
|  |     feature = "nrf52840", | ||||||
|  | )))] | ||||||
|  | compile_error!("No chip feature activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840"); | ||||||
|  | 
 | ||||||
|  | #[cfg(any(
 | ||||||
|  |     all(feature = "nrf52810", feature = "nrf52811"), | ||||||
|  |     all(feature = "nrf52810", feature = "nrf52832"), | ||||||
|  |     all(feature = "nrf52810", feature = "nrf52833"), | ||||||
|  |     all(feature = "nrf52810", feature = "nrf52840"), | ||||||
|  |     all(feature = "nrf52811", feature = "nrf52832"), | ||||||
|  |     all(feature = "nrf52811", feature = "nrf52833"), | ||||||
|  |     all(feature = "nrf52811", feature = "nrf52840"), | ||||||
|  |     all(feature = "nrf52832", feature = "nrf52833"), | ||||||
|  |     all(feature = "nrf52832", feature = "nrf52840"), | ||||||
|  |     all(feature = "nrf52833", feature = "nrf52840"), | ||||||
|  | ))] | ||||||
|  | compile_error!("Multile chip features activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840"); | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "nrf52810")] | ||||||
|  | pub use nrf52810_pac as pac; | ||||||
|  | #[cfg(feature = "nrf52811")] | ||||||
|  | pub use nrf52811_pac as pac; | ||||||
|  | #[cfg(feature = "nrf52832")] | ||||||
|  | pub use nrf52832_pac as pac; | ||||||
|  | #[cfg(feature = "nrf52833")] | ||||||
|  | pub use nrf52833_pac as pac; | ||||||
|  | #[cfg(feature = "nrf52840")] | ||||||
|  | pub use nrf52840_pac as pac; | ||||||
|  | 
 | ||||||
|  | pub mod interrupt; | ||||||
|  | pub mod qspi; | ||||||
|  | pub mod uarte; | ||||||
|  | pub use cortex_m_rt::interrupt; | ||||||
							
								
								
									
										322
									
								
								embassy-nrf/src/qspi.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								embassy-nrf/src/qspi.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,322 @@ | |||||||
|  | use crate::pac::{Interrupt, QSPI}; | ||||||
|  | use core::future::Future; | ||||||
|  | use nrf52840_hal::gpio::{Output, Pin as GpioPin, Port as GpioPort, PushPull}; | ||||||
|  | 
 | ||||||
|  | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; | ||||||
|  | pub use crate::pac::qspi::ifconfig0::PPSIZE_A as WritePageSize; | ||||||
|  | pub use crate::pac::qspi::ifconfig0::READOC_A as ReadOpcode; | ||||||
|  | pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode; | ||||||
|  | 
 | ||||||
|  | // TODO
 | ||||||
|  | // - config:
 | ||||||
|  | //   - 32bit address mode
 | ||||||
|  | //   - SPI freq
 | ||||||
|  | //   - SPI sck delay
 | ||||||
|  | //   - Deep power down mode (DPM)
 | ||||||
|  | //   - SPI mode 3
 | ||||||
|  | // - activate/deactivate
 | ||||||
|  | // - set gpio in high drive
 | ||||||
|  | 
 | ||||||
|  | use embassy::flash::{Error, Flash}; | ||||||
|  | use embassy::util::{DropBomb, Signal}; | ||||||
|  | 
 | ||||||
|  | use crate::interrupt; | ||||||
|  | 
 | ||||||
|  | pub struct Pins { | ||||||
|  |     pub sck: GpioPin<Output<PushPull>>, | ||||||
|  |     pub csn: GpioPin<Output<PushPull>>, | ||||||
|  |     pub io0: GpioPin<Output<PushPull>>, | ||||||
|  |     pub io1: GpioPin<Output<PushPull>>, | ||||||
|  |     pub io2: Option<GpioPin<Output<PushPull>>>, | ||||||
|  |     pub io3: Option<GpioPin<Output<PushPull>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Config { | ||||||
|  |     pub pins: Pins, | ||||||
|  |     pub xip_offset: u32, | ||||||
|  |     pub read_opcode: ReadOpcode, | ||||||
|  |     pub write_opcode: WriteOpcode, | ||||||
|  |     pub write_page_size: WritePageSize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Qspi { | ||||||
|  |     inner: QSPI, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn port_bit(port: GpioPort) -> bool { | ||||||
|  |     match port { | ||||||
|  |         GpioPort::Port0 => false, | ||||||
|  |         GpioPort::Port1 => true, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Qspi { | ||||||
|  |     pub fn new(qspi: QSPI, config: Config) -> Self { | ||||||
|  |         qspi.psel.sck.write(|w| { | ||||||
|  |             let pin = &config.pins.sck; | ||||||
|  |             let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  |         qspi.psel.csn.write(|w| { | ||||||
|  |             let pin = &config.pins.csn; | ||||||
|  |             let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  |         qspi.psel.io0.write(|w| { | ||||||
|  |             let pin = &config.pins.io0; | ||||||
|  |             let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  |         qspi.psel.io1.write(|w| { | ||||||
|  |             let pin = &config.pins.io1; | ||||||
|  |             let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  |         qspi.psel.io2.write(|w| { | ||||||
|  |             if let Some(ref pin) = config.pins.io2 { | ||||||
|  |                 let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |                 let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |                 w.connect().connected() | ||||||
|  |             } else { | ||||||
|  |                 w.connect().disconnected() | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         qspi.psel.io3.write(|w| { | ||||||
|  |             if let Some(ref pin) = config.pins.io3 { | ||||||
|  |                 let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |                 let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |                 w.connect().connected() | ||||||
|  |             } else { | ||||||
|  |                 w.connect().disconnected() | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         qspi.ifconfig0.write(|w| { | ||||||
|  |             let w = w.addrmode().variant(AddressMode::_24BIT); | ||||||
|  |             let w = w.dpmenable().disable(); | ||||||
|  |             let w = w.ppsize().variant(config.write_page_size); | ||||||
|  |             let w = w.readoc().variant(config.read_opcode); | ||||||
|  |             let w = w.writeoc().variant(config.write_opcode); | ||||||
|  |             w | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         qspi.ifconfig1.write(|w| { | ||||||
|  |             let w = unsafe { w.sckdelay().bits(80) }; | ||||||
|  |             let w = w.dpmen().exit(); | ||||||
|  |             let w = w.spimode().mode0(); | ||||||
|  |             let w = unsafe { w.sckfreq().bits(3) }; | ||||||
|  |             w | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         qspi.xipoffset | ||||||
|  |             .write(|w| unsafe { w.xipoffset().bits(config.xip_offset) }); | ||||||
|  | 
 | ||||||
|  |         // Enable it
 | ||||||
|  |         qspi.enable.write(|w| w.enable().enabled()); | ||||||
|  | 
 | ||||||
|  |         qspi.events_ready.reset(); | ||||||
|  |         qspi.tasks_activate.write(|w| w.tasks_activate().bit(true)); | ||||||
|  |         while qspi.events_ready.read().bits() == 0 {} | ||||||
|  |         qspi.events_ready.reset(); | ||||||
|  | 
 | ||||||
|  |         // Enable READY interrupt
 | ||||||
|  |         qspi.intenset.write(|w| w.ready().set()); | ||||||
|  |         interrupt::set_priority(Interrupt::QSPI, interrupt::Priority::Level7); | ||||||
|  |         interrupt::enable(Interrupt::QSPI); | ||||||
|  | 
 | ||||||
|  |         Self { inner: qspi } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn custom_instruction<'a>( | ||||||
|  |         &'a mut self, | ||||||
|  |         opcode: u8, | ||||||
|  |         req: &'a [u8], | ||||||
|  |         resp: &'a mut [u8], | ||||||
|  |     ) -> impl Future<Output = Result<(), Error>> + 'a { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             assert!(req.len() <= 8); | ||||||
|  |             assert!(resp.len() <= 8); | ||||||
|  | 
 | ||||||
|  |             let mut dat0: u32 = 0; | ||||||
|  |             let mut dat1: u32 = 0; | ||||||
|  | 
 | ||||||
|  |             for i in 0..4 { | ||||||
|  |                 if i < req.len() { | ||||||
|  |                     dat0 |= (req[i] as u32) << (i * 8); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             for i in 0..4 { | ||||||
|  |                 if i + 4 < req.len() { | ||||||
|  |                     dat1 |= (req[i + 4] as u32) << (i * 8); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let len = core::cmp::max(req.len(), resp.len()) as u8; | ||||||
|  | 
 | ||||||
|  |             self.inner.cinstrdat0.write(|w| unsafe { w.bits(dat0) }); | ||||||
|  |             self.inner.cinstrdat1.write(|w| unsafe { w.bits(dat1) }); | ||||||
|  |             self.inner.events_ready.reset(); | ||||||
|  |             self.inner.cinstrconf.write(|w| { | ||||||
|  |                 let w = unsafe { w.opcode().bits(opcode) }; | ||||||
|  |                 let w = unsafe { w.length().bits(len + 1) }; | ||||||
|  |                 let w = w.lio2().bit(true); | ||||||
|  |                 let w = w.lio3().bit(true); | ||||||
|  |                 let w = w.wipwait().bit(true); | ||||||
|  |                 let w = w.wren().bit(true); | ||||||
|  |                 let w = w.lfen().bit(false); | ||||||
|  |                 let w = w.lfstop().bit(false); | ||||||
|  |                 w | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             SIGNAL.wait().await; | ||||||
|  | 
 | ||||||
|  |             let dat0 = self.inner.cinstrdat0.read().bits(); | ||||||
|  |             let dat1 = self.inner.cinstrdat1.read().bits(); | ||||||
|  |             for i in 0..4 { | ||||||
|  |                 if i < resp.len() { | ||||||
|  |                     resp[i] = (dat0 >> (i * 8)) as u8; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             for i in 0..4 { | ||||||
|  |                 if i + 4 < resp.len() { | ||||||
|  |                     resp[i] = (dat1 >> (i * 8)) as u8; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Flash for Qspi { | ||||||
|  |     type ReadFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||||||
|  |     type WriteFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||||||
|  |     type ErasePageFuture<'a> = impl Future<Output = Result<(), Error>> + 'a; | ||||||
|  | 
 | ||||||
|  |     fn read<'a>(&'a mut self, address: usize, data: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             assert_eq!(data.as_ptr() as u32 % 4, 0); | ||||||
|  |             assert_eq!(data.len() as u32 % 4, 0); | ||||||
|  |             assert_eq!(address as u32 % 4, 0); | ||||||
|  | 
 | ||||||
|  |             self.inner | ||||||
|  |                 .read | ||||||
|  |                 .src | ||||||
|  |                 .write(|w| unsafe { w.src().bits(address as u32) }); | ||||||
|  |             self.inner | ||||||
|  |                 .read | ||||||
|  |                 .dst | ||||||
|  |                 .write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); | ||||||
|  |             self.inner | ||||||
|  |                 .read | ||||||
|  |                 .cnt | ||||||
|  |                 .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); | ||||||
|  | 
 | ||||||
|  |             self.inner.events_ready.reset(); | ||||||
|  |             self.inner | ||||||
|  |                 .tasks_readstart | ||||||
|  |                 .write(|w| w.tasks_readstart().bit(true)); | ||||||
|  | 
 | ||||||
|  |             SIGNAL.wait().await; | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write<'a>(&'a mut self, address: usize, data: &'a [u8]) -> Self::WriteFuture<'a> { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             assert_eq!(data.as_ptr() as u32 % 4, 0); | ||||||
|  |             assert_eq!(data.len() as u32 % 4, 0); | ||||||
|  |             assert_eq!(address as u32 % 4, 0); | ||||||
|  | 
 | ||||||
|  |             self.inner | ||||||
|  |                 .write | ||||||
|  |                 .src | ||||||
|  |                 .write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); | ||||||
|  |             self.inner | ||||||
|  |                 .write | ||||||
|  |                 .dst | ||||||
|  |                 .write(|w| unsafe { w.dst().bits(address as u32) }); | ||||||
|  |             self.inner | ||||||
|  |                 .write | ||||||
|  |                 .cnt | ||||||
|  |                 .write(|w| unsafe { w.cnt().bits(data.len() as u32) }); | ||||||
|  | 
 | ||||||
|  |             self.inner.events_ready.reset(); | ||||||
|  |             self.inner | ||||||
|  |                 .tasks_writestart | ||||||
|  |                 .write(|w| w.tasks_writestart().bit(true)); | ||||||
|  | 
 | ||||||
|  |             SIGNAL.wait().await; | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a> { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             assert_eq!(address as u32 % 4096, 0); | ||||||
|  | 
 | ||||||
|  |             self.inner | ||||||
|  |                 .erase | ||||||
|  |                 .ptr | ||||||
|  |                 .write(|w| unsafe { w.ptr().bits(address as u32) }); | ||||||
|  |             self.inner.erase.len.write(|w| w.len()._4kb()); | ||||||
|  |             self.inner.events_ready.reset(); | ||||||
|  |             self.inner | ||||||
|  |                 .tasks_erasestart | ||||||
|  |                 .write(|w| w.tasks_erasestart().bit(true)); | ||||||
|  | 
 | ||||||
|  |             SIGNAL.wait().await; | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn size(&self) -> usize { | ||||||
|  |         256 * 4096 // TODO
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_size(&self) -> usize { | ||||||
|  |         4 // TODO
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write_size(&self) -> usize { | ||||||
|  |         4 // TODO
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn erase_size(&self) -> usize { | ||||||
|  |         4096 // TODO
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SIGNAL: Signal<()> = Signal::new(); | ||||||
|  | 
 | ||||||
|  | #[interrupt] | ||||||
|  | unsafe fn QSPI() { | ||||||
|  |     let p = unsafe { crate::pac::Peripherals::steal().QSPI }; | ||||||
|  |     if p.events_ready.read().events_ready().bit_is_set() { | ||||||
|  |         p.events_ready.reset(); | ||||||
|  |         SIGNAL.signal(()); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										550
									
								
								embassy-nrf/src/uarte.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								embassy-nrf/src/uarte.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,550 @@ | |||||||
|  | //! HAL interface to the UARTE peripheral
 | ||||||
|  | //!
 | ||||||
|  | //! See product specification:
 | ||||||
|  | //!
 | ||||||
|  | //! - nrf52832: Section 35
 | ||||||
|  | //! - nrf52840: Section 6.34
 | ||||||
|  | use core::cell::UnsafeCell; | ||||||
|  | use core::cmp::min; | ||||||
|  | use core::marker::PhantomPinned; | ||||||
|  | use core::ops::Deref; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use core::ptr; | ||||||
|  | use core::sync::atomic::{compiler_fence, Ordering}; | ||||||
|  | use core::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use crate::interrupt; | ||||||
|  | use crate::interrupt::CriticalSection; | ||||||
|  | use crate::pac::{uarte0, Interrupt, UARTE0, UARTE1}; | ||||||
|  | use embedded_hal::digital::v2::OutputPin; | ||||||
|  | use nrf52840_hal::gpio::{Floating, Input, Output, Pin as GpioPin, Port as GpioPort, PushPull}; | ||||||
|  | 
 | ||||||
|  | // Re-export SVD variants to allow user to directly set values
 | ||||||
|  | pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; | ||||||
|  | 
 | ||||||
|  | use embassy::io::{AsyncBufRead, AsyncWrite, Result}; | ||||||
|  | use embassy::util::WakerStore; | ||||||
|  | 
 | ||||||
|  | use defmt::trace; | ||||||
|  | 
 | ||||||
|  | //use crate::trace;
 | ||||||
|  | 
 | ||||||
|  | const RINGBUF_SIZE: usize = 512; | ||||||
|  | struct RingBuf { | ||||||
|  |     buf: [u8; RINGBUF_SIZE], | ||||||
|  |     start: usize, | ||||||
|  |     end: usize, | ||||||
|  |     empty: bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl RingBuf { | ||||||
|  |     fn new() -> Self { | ||||||
|  |         RingBuf { | ||||||
|  |             buf: [0; RINGBUF_SIZE], | ||||||
|  |             start: 0, | ||||||
|  |             end: 0, | ||||||
|  |             empty: true, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn push_buf(&mut self) -> &mut [u8] { | ||||||
|  |         if self.start == self.end && !self.empty { | ||||||
|  |             trace!("  ringbuf: push_buf empty"); | ||||||
|  |             return &mut self.buf[..0]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let n = if self.start <= self.end { | ||||||
|  |             RINGBUF_SIZE - self.end | ||||||
|  |         } else { | ||||||
|  |             self.start - self.end | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         trace!("  ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); | ||||||
|  |         &mut self.buf[self.end..self.end + n] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn push(&mut self, n: usize) { | ||||||
|  |         trace!("  ringbuf: push {:?}", n); | ||||||
|  |         if n == 0 { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         self.end = Self::wrap(self.end + n); | ||||||
|  |         self.empty = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn pop_buf(&mut self) -> &mut [u8] { | ||||||
|  |         if self.empty { | ||||||
|  |             trace!("  ringbuf: pop_buf empty"); | ||||||
|  |             return &mut self.buf[..0]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let n = if self.end <= self.start { | ||||||
|  |             RINGBUF_SIZE - self.start | ||||||
|  |         } else { | ||||||
|  |             self.end - self.start | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         trace!("  ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); | ||||||
|  |         &mut self.buf[self.start..self.start + n] | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn pop(&mut self, n: usize) { | ||||||
|  |         trace!("  ringbuf: pop {:?}", n); | ||||||
|  |         if n == 0 { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         self.start = Self::wrap(self.start + n); | ||||||
|  |         self.empty = self.start == self.end; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn wrap(n: usize) -> usize { | ||||||
|  |         assert!(n <= RINGBUF_SIZE); | ||||||
|  |         if n == RINGBUF_SIZE { | ||||||
|  |             0 | ||||||
|  |         } else { | ||||||
|  |             n | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Copy, Clone, Debug, PartialEq)] | ||||||
|  | enum RxState { | ||||||
|  |     Idle, | ||||||
|  |     Receiving, | ||||||
|  |     ReceivingReady, | ||||||
|  |     Stopping, | ||||||
|  | } | ||||||
|  | #[derive(Copy, Clone, Debug, PartialEq)] | ||||||
|  | enum TxState { | ||||||
|  |     Idle, | ||||||
|  |     Transmitting(usize), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Interface to a UARTE instance
 | ||||||
|  | ///
 | ||||||
|  | /// This is a very basic interface that comes with the following limitations:
 | ||||||
|  | /// - The UARTE instances share the same address space with instances of UART.
 | ||||||
|  | ///   You need to make sure that conflicting instances
 | ||||||
|  | ///   are disabled before using `Uarte`. See product specification:
 | ||||||
|  | ///     - nrf52832: Section 15.2
 | ||||||
|  | ///     - nrf52840: Section 6.1.2
 | ||||||
|  | pub struct Uarte<T: Instance> { | ||||||
|  |     started: bool, | ||||||
|  |     state: UnsafeCell<UarteState<T>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // public because it needs to be used in Instance::{get_state, set_state}, but
 | ||||||
|  | // should not be used outside the module
 | ||||||
|  | #[doc(hidden)] | ||||||
|  | pub struct UarteState<T> { | ||||||
|  |     inner: T, | ||||||
|  | 
 | ||||||
|  |     rx: RingBuf, | ||||||
|  |     rx_state: RxState, | ||||||
|  |     rx_waker: WakerStore, | ||||||
|  | 
 | ||||||
|  |     tx: RingBuf, | ||||||
|  |     tx_state: TxState, | ||||||
|  |     tx_waker: WakerStore, | ||||||
|  | 
 | ||||||
|  |     _pin: PhantomPinned, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn port_bit(port: GpioPort) -> bool { | ||||||
|  |     match port { | ||||||
|  |         GpioPort::Port0 => false, | ||||||
|  |         GpioPort::Port1 => true, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Instance> Uarte<T> { | ||||||
|  |     pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self { | ||||||
|  |         // Select pins
 | ||||||
|  |         uarte.psel.rxd.write(|w| { | ||||||
|  |             let w = unsafe { w.pin().bits(pins.rxd.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pins.rxd.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  |         pins.txd.set_high().unwrap(); | ||||||
|  |         uarte.psel.txd.write(|w| { | ||||||
|  |             let w = unsafe { w.pin().bits(pins.txd.pin()) }; | ||||||
|  |             let w = w.port().bit(port_bit(pins.txd.port())); | ||||||
|  |             w.connect().connected() | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Optional pins
 | ||||||
|  |         uarte.psel.cts.write(|w| { | ||||||
|  |             if let Some(ref pin) = pins.cts { | ||||||
|  |                 let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |                 let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |                 w.connect().connected() | ||||||
|  |             } else { | ||||||
|  |                 w.connect().disconnected() | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         uarte.psel.rts.write(|w| { | ||||||
|  |             if let Some(ref pin) = pins.rts { | ||||||
|  |                 let w = unsafe { w.pin().bits(pin.pin()) }; | ||||||
|  |                 let w = w.port().bit(port_bit(pin.port())); | ||||||
|  |                 w.connect().connected() | ||||||
|  |             } else { | ||||||
|  |                 w.connect().disconnected() | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Enable UARTE instance
 | ||||||
|  |         uarte.enable.write(|w| w.enable().enabled()); | ||||||
|  | 
 | ||||||
|  |         // Enable interrupts
 | ||||||
|  |         uarte.intenset.write(|w| w.endrx().set().endtx().set()); | ||||||
|  | 
 | ||||||
|  |         // Configure
 | ||||||
|  |         let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); | ||||||
|  |         uarte | ||||||
|  |             .config | ||||||
|  |             .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); | ||||||
|  | 
 | ||||||
|  |         // Configure frequency
 | ||||||
|  |         uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); | ||||||
|  | 
 | ||||||
|  |         Uarte { | ||||||
|  |             started: false, | ||||||
|  |             state: UnsafeCell::new(UarteState { | ||||||
|  |                 inner: uarte, | ||||||
|  | 
 | ||||||
|  |                 rx: RingBuf::new(), | ||||||
|  |                 rx_state: RxState::Idle, | ||||||
|  |                 rx_waker: WakerStore::new(), | ||||||
|  | 
 | ||||||
|  |                 tx: RingBuf::new(), | ||||||
|  |                 tx_state: TxState::Idle, | ||||||
|  |                 tx_waker: WakerStore::new(), | ||||||
|  | 
 | ||||||
|  |                 _pin: PhantomPinned, | ||||||
|  |             }), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn with_state<'a, R>( | ||||||
|  |         self: Pin<&'a mut Self>, | ||||||
|  |         f: impl FnOnce(Pin<&'a mut UarteState<T>>) -> R, | ||||||
|  |     ) -> R { | ||||||
|  |         let Self { state, started } = unsafe { self.get_unchecked_mut() }; | ||||||
|  | 
 | ||||||
|  |         interrupt::free(|cs| { | ||||||
|  |             let ptr = state.get(); | ||||||
|  | 
 | ||||||
|  |             if !*started { | ||||||
|  |                 T::set_state(cs, ptr); | ||||||
|  | 
 | ||||||
|  |                 *started = true; | ||||||
|  | 
 | ||||||
|  |                 // safety: safe because critical section ensures only one *mut UartState
 | ||||||
|  |                 // exists at the same time.
 | ||||||
|  |                 unsafe { Pin::new_unchecked(&mut *ptr) }.start(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // safety: safe because critical section ensures only one *mut UartState
 | ||||||
|  |             // exists at the same time.
 | ||||||
|  |             f(unsafe { Pin::new_unchecked(&mut *ptr) }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Instance> Drop for Uarte<T> { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         // stop DMA before dropping, because DMA is using the buffer in `self`.
 | ||||||
|  |         todo!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Instance> AsyncBufRead for Uarte<T> { | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |         self.with_state(|s| s.poll_fill_buf(cx)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize) { | ||||||
|  |         self.with_state(|s| s.consume(amt)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Instance> AsyncWrite for Uarte<T> { | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||||||
|  |         self.with_state(|s| s.poll_write(cx, buf)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: Instance> UarteState<T> { | ||||||
|  |     pub fn start(self: Pin<&mut Self>) { | ||||||
|  |         interrupt::set_priority(T::interrupt(), interrupt::Priority::Level7); | ||||||
|  |         interrupt::enable(T::interrupt()); | ||||||
|  |         interrupt::pend(T::interrupt()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |         let this = unsafe { self.get_unchecked_mut() }; | ||||||
|  | 
 | ||||||
|  |         // Conservative compiler fence to prevent optimizations that do not
 | ||||||
|  |         // take in to account actions by DMA. The fence has been placed here,
 | ||||||
|  |         // before any DMA action has started
 | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  |         trace!("poll_read"); | ||||||
|  | 
 | ||||||
|  |         // We have data ready in buffer? Return it.
 | ||||||
|  |         let buf = this.rx.pop_buf(); | ||||||
|  |         if buf.len() != 0 { | ||||||
|  |             trace!("  got {:?} {:?}", buf.as_ptr() as u32, buf.len()); | ||||||
|  |             return Poll::Ready(Ok(buf)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         trace!("  empty"); | ||||||
|  | 
 | ||||||
|  |         if this.rx_state == RxState::ReceivingReady { | ||||||
|  |             trace!("  stopping"); | ||||||
|  |             this.rx_state = RxState::Stopping; | ||||||
|  |             this.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.rx_waker.store(cx.waker()); | ||||||
|  |         Poll::Pending | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize) { | ||||||
|  |         let this = unsafe { self.get_unchecked_mut() }; | ||||||
|  |         trace!("consume {:?}", amt); | ||||||
|  |         this.rx.pop(amt); | ||||||
|  |         interrupt::pend(T::interrupt()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||||||
|  |         let this = unsafe { self.get_unchecked_mut() }; | ||||||
|  | 
 | ||||||
|  |         trace!("poll_write: {:?}", buf.len()); | ||||||
|  | 
 | ||||||
|  |         let tx_buf = this.tx.push_buf(); | ||||||
|  |         if tx_buf.len() == 0 { | ||||||
|  |             trace!("poll_write: pending"); | ||||||
|  |             this.tx_waker.store(cx.waker()); | ||||||
|  |             return Poll::Pending; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let n = min(tx_buf.len(), buf.len()); | ||||||
|  |         tx_buf[..n].copy_from_slice(&buf[..n]); | ||||||
|  |         this.tx.push(n); | ||||||
|  | 
 | ||||||
|  |         trace!("poll_write: queued {:?}", n); | ||||||
|  | 
 | ||||||
|  |         // Conservative compiler fence to prevent optimizations that do not
 | ||||||
|  |         // take in to account actions by DMA. The fence has been placed here,
 | ||||||
|  |         // before any DMA action has started
 | ||||||
|  |         compiler_fence(Ordering::SeqCst); | ||||||
|  | 
 | ||||||
|  |         interrupt::pend(T::interrupt()); | ||||||
|  | 
 | ||||||
|  |         Poll::Ready(Ok(n)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn on_interrupt(&mut self) { | ||||||
|  |         trace!("irq: start"); | ||||||
|  |         let mut more_work = true; | ||||||
|  |         while more_work { | ||||||
|  |             more_work = false; | ||||||
|  |             match self.rx_state { | ||||||
|  |                 RxState::Idle => { | ||||||
|  |                     trace!("  irq_rx: in state idle"); | ||||||
|  | 
 | ||||||
|  |                     if self.inner.events_rxdrdy.read().bits() != 0 { | ||||||
|  |                         trace!("  irq_rx: rxdrdy?????"); | ||||||
|  |                         self.inner.events_rxdrdy.reset(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if self.inner.events_endrx.read().bits() != 0 { | ||||||
|  |                         panic!("unexpected endrx"); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     let buf = self.rx.push_buf(); | ||||||
|  |                     if buf.len() != 0 { | ||||||
|  |                         trace!("  irq_rx: starting {:?}", buf.len()); | ||||||
|  |                         self.rx_state = RxState::Receiving; | ||||||
|  | 
 | ||||||
|  |                         // Set up the DMA read
 | ||||||
|  |                         self.inner.rxd.ptr.write(|w| | ||||||
|  |                             // The PTR field is a full 32 bits wide and accepts the full range
 | ||||||
|  |                             // of values.
 | ||||||
|  |                             unsafe { w.ptr().bits(buf.as_ptr() as u32) }); | ||||||
|  |                         self.inner.rxd.maxcnt.write(|w| | ||||||
|  |                             // We're giving it the length of the buffer, so no danger of
 | ||||||
|  |                             // accessing invalid memory. We have verified that the length of the
 | ||||||
|  |                             // buffer fits in an `u8`, so the cast to `u8` is also fine.
 | ||||||
|  |                             //
 | ||||||
|  |                             // The MAXCNT field is at least 8 bits wide and accepts the full
 | ||||||
|  |                             // range of values.
 | ||||||
|  |                             unsafe { w.maxcnt().bits(buf.len() as _) }); | ||||||
|  |                         trace!("  irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len()); | ||||||
|  | 
 | ||||||
|  |                         // Enable RXRDY interrupt.
 | ||||||
|  |                         self.inner.events_rxdrdy.reset(); | ||||||
|  |                         self.inner.intenset.write(|w| w.rxdrdy().set()); | ||||||
|  | 
 | ||||||
|  |                         // Start UARTE Receive transaction
 | ||||||
|  |                         self.inner.tasks_startrx.write(|w| | ||||||
|  |                             // `1` is a valid value to write to task registers.
 | ||||||
|  |                             unsafe { w.bits(1) }); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 RxState::Receiving => { | ||||||
|  |                     trace!("  irq_rx: in state receiving"); | ||||||
|  |                     if self.inner.events_rxdrdy.read().bits() != 0 { | ||||||
|  |                         trace!("  irq_rx: rxdrdy"); | ||||||
|  | 
 | ||||||
|  |                         // Disable the RXRDY event interrupt
 | ||||||
|  |                         // RXRDY is triggered for every byte, but we only care about whether we have
 | ||||||
|  |                         // some bytes or not. So as soon as we have at least one, disable it, to avoid
 | ||||||
|  |                         // wasting CPU cycles in interrupts.
 | ||||||
|  |                         self.inner.intenclr.write(|w| w.rxdrdy().clear()); | ||||||
|  | 
 | ||||||
|  |                         self.inner.events_rxdrdy.reset(); | ||||||
|  | 
 | ||||||
|  |                         self.rx_waker.wake(); | ||||||
|  |                         self.rx_state = RxState::ReceivingReady; | ||||||
|  |                         more_work = true; // in case we also have endrx pending
 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 RxState::ReceivingReady | RxState::Stopping => { | ||||||
|  |                     trace!("  irq_rx: in state ReceivingReady"); | ||||||
|  | 
 | ||||||
|  |                     if self.inner.events_rxdrdy.read().bits() != 0 { | ||||||
|  |                         trace!("  irq_rx: rxdrdy"); | ||||||
|  |                         self.inner.events_rxdrdy.reset(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if self.inner.events_endrx.read().bits() != 0 { | ||||||
|  |                         let n: usize = self.inner.rxd.amount.read().amount().bits() as usize; | ||||||
|  |                         trace!("  irq_rx: endrx {:?}", n); | ||||||
|  |                         self.rx.push(n); | ||||||
|  | 
 | ||||||
|  |                         self.inner.events_endrx.reset(); | ||||||
|  | 
 | ||||||
|  |                         self.rx_waker.wake(); | ||||||
|  |                         self.rx_state = RxState::Idle; | ||||||
|  |                         more_work = true; // start another rx if possible
 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         more_work = true; | ||||||
|  |         while more_work { | ||||||
|  |             more_work = false; | ||||||
|  |             match self.tx_state { | ||||||
|  |                 TxState::Idle => { | ||||||
|  |                     trace!("  irq_tx: in state Idle"); | ||||||
|  |                     let buf = self.tx.pop_buf(); | ||||||
|  |                     if buf.len() != 0 { | ||||||
|  |                         trace!("  irq_tx: starting {:?}", buf.len()); | ||||||
|  |                         self.tx_state = TxState::Transmitting(buf.len()); | ||||||
|  | 
 | ||||||
|  |                         // Set up the DMA write
 | ||||||
|  |                         self.inner.txd.ptr.write(|w| | ||||||
|  |                             // The PTR field is a full 32 bits wide and accepts the full range
 | ||||||
|  |                             // of values.
 | ||||||
|  |                             unsafe { w.ptr().bits(buf.as_ptr() as u32) }); | ||||||
|  |                         self.inner.txd.maxcnt.write(|w| | ||||||
|  |                             // We're giving it the length of the buffer, so no danger of
 | ||||||
|  |                             // accessing invalid memory. We have verified that the length of the
 | ||||||
|  |                             // buffer fits in an `u8`, so the cast to `u8` is also fine.
 | ||||||
|  |                             //
 | ||||||
|  |                             // The MAXCNT field is 8 bits wide and accepts the full range of
 | ||||||
|  |                             // values.
 | ||||||
|  |                             unsafe { w.maxcnt().bits(buf.len() as _) }); | ||||||
|  | 
 | ||||||
|  |                         // Start UARTE Transmit transaction
 | ||||||
|  |                         self.inner.tasks_starttx.write(|w| | ||||||
|  |                             // `1` is a valid value to write to task registers.
 | ||||||
|  |                             unsafe { w.bits(1) }); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 TxState::Transmitting(n) => { | ||||||
|  |                     trace!("  irq_tx: in state Transmitting"); | ||||||
|  |                     if self.inner.events_endtx.read().bits() != 0 { | ||||||
|  |                         self.inner.events_endtx.reset(); | ||||||
|  | 
 | ||||||
|  |                         trace!("  irq_tx: endtx {:?}", n); | ||||||
|  |                         self.tx.pop(n); | ||||||
|  |                         self.tx_waker.wake(); | ||||||
|  |                         self.tx_state = TxState::Idle; | ||||||
|  |                         more_work = true; // start another tx if possible
 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         trace!("irq: end"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Pins { | ||||||
|  |     pub rxd: GpioPin<Input<Floating>>, | ||||||
|  |     pub txd: GpioPin<Output<PushPull>>, | ||||||
|  |     pub cts: Option<GpioPin<Input<Floating>>>, | ||||||
|  |     pub rts: Option<GpioPin<Output<PushPull>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | mod private { | ||||||
|  |     use nrf52840_pac::{UARTE0, UARTE1}; | ||||||
|  |     pub trait Sealed {} | ||||||
|  | 
 | ||||||
|  |     impl Sealed for UARTE0 {} | ||||||
|  |     impl Sealed for UARTE1 {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait Instance: Deref<Target = uarte0::RegisterBlock> + Sized + private::Sealed { | ||||||
|  |     fn interrupt() -> Interrupt; | ||||||
|  | 
 | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self>; | ||||||
|  | 
 | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[interrupt] | ||||||
|  | unsafe fn UARTE0_UART0() { | ||||||
|  |     interrupt::free(|cs| UARTE0::get_state(cs).as_mut().unwrap().on_interrupt()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[interrupt] | ||||||
|  | unsafe fn UARTE1() { | ||||||
|  |     interrupt::free(|cs| UARTE1::get_state(cs).as_mut().unwrap().on_interrupt()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static mut UARTE0_STATE: *mut UarteState<UARTE0> = ptr::null_mut(); | ||||||
|  | static mut UARTE1_STATE: *mut UarteState<UARTE1> = ptr::null_mut(); | ||||||
|  | 
 | ||||||
|  | impl Instance for UARTE0 { | ||||||
|  |     fn interrupt() -> Interrupt { | ||||||
|  |         Interrupt::UARTE0_UART0 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self> { | ||||||
|  |         unsafe { UARTE0_STATE } // Safe because of CriticalSection
 | ||||||
|  |     } | ||||||
|  |     fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>) { | ||||||
|  |         unsafe { UARTE0_STATE = state } // Safe because of CriticalSection
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Instance for UARTE1 { | ||||||
|  |     fn interrupt() -> Interrupt { | ||||||
|  |         Interrupt::UARTE1 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self> { | ||||||
|  |         unsafe { UARTE1_STATE } // Safe because of CriticalSection
 | ||||||
|  |     } | ||||||
|  |     fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>) { | ||||||
|  |         unsafe { UARTE1_STATE = state } // Safe because of CriticalSection
 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								embassy/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								embassy/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | [package] | ||||||
|  | name = "embassy" | ||||||
|  | version = "0.1.0" | ||||||
|  | authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"] | ||||||
|  | edition = "2018" | ||||||
|  | 
 | ||||||
|  | [features] | ||||||
|  | std = [] | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | defmt = "0.1.0" | ||||||
|  | cortex-m = "0.6.3" | ||||||
|  | futures     = { version = "0.3.5", default-features = false, features = [ "async-await" ] } | ||||||
|  | pin-project = { version = "0.4.23", default-features = false } | ||||||
							
								
								
									
										51
									
								
								embassy/src/flash.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								embassy/src/flash.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | 
 | ||||||
|  | use core::future::Future; | ||||||
|  | 
 | ||||||
|  | #[derive(defmt::Format, Copy, Clone, Debug, Eq, PartialEq)] | ||||||
|  | pub enum Error { | ||||||
|  |     Failed, | ||||||
|  |     AddressMisaligned, | ||||||
|  |     BufferMisaligned, | ||||||
|  | 
 | ||||||
|  |     _NonExhaustive, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait Flash { | ||||||
|  |     type ReadFuture<'a>: Future<Output = Result<(), Error>>; | ||||||
|  |     type WriteFuture<'a>: Future<Output = Result<(), Error>>; | ||||||
|  |     type ErasePageFuture<'a>: Future<Output = Result<(), Error>>; | ||||||
|  | 
 | ||||||
|  |     /// Reads data from the flash device.
 | ||||||
|  |     ///
 | ||||||
|  |     /// address must be a multiple of self.read_size().
 | ||||||
|  |     /// buf.len() must be a multiple of self.read_size().
 | ||||||
|  |     fn read<'a>(&'a mut self, address: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; | ||||||
|  | 
 | ||||||
|  |     /// Writes data to the flash device.
 | ||||||
|  |     ///
 | ||||||
|  |     /// address must be a multiple of self.write_size().
 | ||||||
|  |     /// buf.len() must be a multiple of self.write_size().
 | ||||||
|  |     fn write<'a>(&'a mut self, address: usize, buf: &'a [u8]) -> Self::WriteFuture<'a>; | ||||||
|  | 
 | ||||||
|  |     /// Erases a single page from the flash device.
 | ||||||
|  |     ///
 | ||||||
|  |     /// address must be a multiple of self.erase_size().
 | ||||||
|  |     fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a>; | ||||||
|  | 
 | ||||||
|  |     /// Returns the total size, in bytes.
 | ||||||
|  |     /// This is not guaranteed to be a power of 2.
 | ||||||
|  |     fn size(&self) -> usize; | ||||||
|  | 
 | ||||||
|  |     /// Returns the read size in bytes.
 | ||||||
|  |     /// This is guaranteed to be a power of 2.
 | ||||||
|  |     fn read_size(&self) -> usize; | ||||||
|  | 
 | ||||||
|  |     /// Returns the write size in bytes.
 | ||||||
|  |     /// This is guaranteed to be a power of 2.
 | ||||||
|  |     fn write_size(&self) -> usize; | ||||||
|  | 
 | ||||||
|  |     /// Returns the erase size in bytes.
 | ||||||
|  |     /// This is guaranteed to be a power of 2.
 | ||||||
|  |     fn erase_size(&self) -> usize; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										133
									
								
								embassy/src/io/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								embassy/src/io/error.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,133 @@ | |||||||
|  | #[cfg(feature = "std")] | ||||||
|  | use core::convert::From; | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | use futures::io; | ||||||
|  | 
 | ||||||
|  | /// Categories of errors that can occur.
 | ||||||
|  | ///
 | ||||||
|  | /// This list is intended to grow over time and it is not recommended to
 | ||||||
|  | /// exhaustively match against it.
 | ||||||
|  | #[derive(defmt::Format, Debug, Clone, Copy, PartialEq, Eq)] | ||||||
|  | pub enum Error { | ||||||
|  |     /// An entity was not found, often a file.
 | ||||||
|  |     NotFound, | ||||||
|  |     /// The operation lacked the necessary privileges to complete.
 | ||||||
|  |     PermissionDenied, | ||||||
|  |     /// The connection was refused by the remote server.
 | ||||||
|  |     ConnectionRefused, | ||||||
|  |     /// The connection was reset by the remote server.
 | ||||||
|  |     ConnectionReset, | ||||||
|  |     /// The connection was aborted (terminated) by the remote server.
 | ||||||
|  |     ConnectionAborted, | ||||||
|  |     /// The network operation failed because it was not connected yet.
 | ||||||
|  |     NotConnected, | ||||||
|  |     /// A socket address could not be bound because the address is already in
 | ||||||
|  |     /// use elsewhere.
 | ||||||
|  |     AddrInUse, | ||||||
|  |     /// A nonexistent interface was requested or the requested address was not
 | ||||||
|  |     /// local.
 | ||||||
|  |     AddrNotAvailable, | ||||||
|  |     /// The operation failed because a pipe was closed.
 | ||||||
|  |     BrokenPipe, | ||||||
|  |     /// An entity already exists, often a file.
 | ||||||
|  |     AlreadyExists, | ||||||
|  |     /// The operation needs to block to complete, but the blocking operation was
 | ||||||
|  |     /// requested to not occur.
 | ||||||
|  |     WouldBlock, | ||||||
|  |     /// A parameter was incorrect.
 | ||||||
|  |     InvalidInput, | ||||||
|  |     /// Data not valid for the operation were encountered.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Unlike [`InvalidInput`], this typically means that the operation
 | ||||||
|  |     /// parameters were valid, however the error was caused by malformed
 | ||||||
|  |     /// input data.
 | ||||||
|  |     ///
 | ||||||
|  |     /// For example, a function that reads a file into a string will error with
 | ||||||
|  |     /// `InvalidData` if the file's contents are not valid UTF-8.
 | ||||||
|  |     ///
 | ||||||
|  |     /// [`InvalidInput`]: #variant.InvalidInput
 | ||||||
|  |     InvalidData, | ||||||
|  |     /// The I/O operation's timeout expired, causing it to be canceled.
 | ||||||
|  |     TimedOut, | ||||||
|  |     /// An error returned when an operation could not be completed because a
 | ||||||
|  |     /// call to [`write`] returned [`Ok(0)`].
 | ||||||
|  |     ///
 | ||||||
|  |     /// This typically means that an operation could only succeed if it wrote a
 | ||||||
|  |     /// particular number of bytes but only a smaller number of bytes could be
 | ||||||
|  |     /// written.
 | ||||||
|  |     ///
 | ||||||
|  |     /// [`write`]: ../../std/io/trait.Write.html#tymethod.write
 | ||||||
|  |     /// [`Ok(0)`]: ../../std/io/type.Result.html
 | ||||||
|  |     WriteZero, | ||||||
|  |     /// This operation was interrupted.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Interrupted operations can typically be retried.
 | ||||||
|  |     Interrupted, | ||||||
|  | 
 | ||||||
|  |     /// An error returned when an operation could not be completed because an
 | ||||||
|  |     /// "end of file" was reached prematurely.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This typically means that an operation could only succeed if it read a
 | ||||||
|  |     /// particular number of bytes but only a smaller number of bytes could be
 | ||||||
|  |     /// read.
 | ||||||
|  |     UnexpectedEof, | ||||||
|  | 
 | ||||||
|  |     /// An operation would have read more data if the given buffer was large.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This typically means that the buffer has been filled with the first N bytes
 | ||||||
|  |     /// of the read data.
 | ||||||
|  |     Truncated, | ||||||
|  | 
 | ||||||
|  |     /// Any I/O error not part of this list.
 | ||||||
|  |     Other, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type Result<T> = core::result::Result<T, Error>; | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl From<io::Error> for Error { | ||||||
|  |     fn from(err: io::Error) -> Error { | ||||||
|  |         match err.kind() { | ||||||
|  |             io::ErrorKind::NotFound => Error::NotFound, | ||||||
|  |             io::ErrorKind::PermissionDenied => Error::PermissionDenied, | ||||||
|  |             io::ErrorKind::ConnectionRefused => Error::ConnectionRefused, | ||||||
|  |             io::ErrorKind::ConnectionReset => Error::ConnectionReset, | ||||||
|  |             io::ErrorKind::ConnectionAborted => Error::ConnectionAborted, | ||||||
|  |             io::ErrorKind::NotConnected => Error::NotConnected, | ||||||
|  |             io::ErrorKind::AddrInUse => Error::AddrInUse, | ||||||
|  |             io::ErrorKind::AddrNotAvailable => Error::AddrNotAvailable, | ||||||
|  |             io::ErrorKind::BrokenPipe => Error::BrokenPipe, | ||||||
|  |             io::ErrorKind::AlreadyExists => Error::AlreadyExists, | ||||||
|  |             io::ErrorKind::WouldBlock => Error::WouldBlock, | ||||||
|  |             io::ErrorKind::InvalidInput => Error::InvalidInput, | ||||||
|  |             io::ErrorKind::InvalidData => Error::InvalidData, | ||||||
|  |             io::ErrorKind::TimedOut => Error::TimedOut, | ||||||
|  |             io::ErrorKind::WriteZero => Error::WriteZero, | ||||||
|  |             io::ErrorKind::Interrupted => Error::Interrupted, | ||||||
|  |             io::ErrorKind::UnexpectedEof => Error::UnexpectedEof, | ||||||
|  |             _ => Error::Other, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl std::error::Error for Error {} | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | impl From<smoltcp::Error> for Error { | ||||||
|  |     fn from(err: smoltcp::Error) -> Error { | ||||||
|  |         match err { | ||||||
|  |             smoltcp::Error::Exhausted => Error::Exhausted, | ||||||
|  |             smoltcp::Error::Illegal => Error::Illegal, | ||||||
|  |             smoltcp::Error::Unaddressable => Error::Unaddressable, | ||||||
|  |             smoltcp::Error::Truncated => Error::Truncated, | ||||||
|  |             smoltcp::Error::Checksum => Error::Checksum, | ||||||
|  |             smoltcp::Error::Unrecognized => Error::Unrecognized, | ||||||
|  |             smoltcp::Error::Fragmented => Error::Fragmented, | ||||||
|  |             smoltcp::Error::Malformed => Error::Malformed, | ||||||
|  |             smoltcp::Error::Dropped => Error::Dropped, | ||||||
|  |             _ => Error::Other, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | */ | ||||||
							
								
								
									
										7
									
								
								embassy/src/io/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								embassy/src/io/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | mod error; | ||||||
|  | mod traits; | ||||||
|  | mod util; | ||||||
|  | 
 | ||||||
|  | pub use self::error::*; | ||||||
|  | pub use self::traits::*; | ||||||
|  | pub use self::util::*; | ||||||
							
								
								
									
										197
									
								
								embassy/src/io/traits.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								embassy/src/io/traits.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,197 @@ | |||||||
|  | 
 | ||||||
|  | use core::ops::DerefMut; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use core::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "alloc")] | ||||||
|  | use alloc::boxed::Box; | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | use futures::io as std_io; | ||||||
|  | 
 | ||||||
|  | use super::error::Result; | ||||||
|  | 
 | ||||||
|  | /// Read bytes asynchronously.
 | ||||||
|  | ///
 | ||||||
|  | /// This trait is analogous to the `std::io::BufRead` trait, but integrates
 | ||||||
|  | /// with the asynchronous task system. In particular, the `poll_fill_buf`
 | ||||||
|  | /// method, unlike `BufRead::fill_buf`, will automatically queue the current task
 | ||||||
|  | /// for wakeup and return if data is not yet available, rather than blocking
 | ||||||
|  | /// the calling thread.
 | ||||||
|  | pub trait AsyncBufRead { | ||||||
|  |     /// Attempt to return the contents of the internal buffer, filling it with more data
 | ||||||
|  |     /// from the inner reader if it is empty.
 | ||||||
|  |     ///
 | ||||||
|  |     /// On success, returns `Poll::Ready(Ok(buf))`.
 | ||||||
|  |     ///
 | ||||||
|  |     /// If no data is available for reading, the method returns
 | ||||||
|  |     /// `Poll::Pending` and arranges for the current task (via
 | ||||||
|  |     /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
 | ||||||
|  |     /// readable or is closed.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This function is a lower-level call. It needs to be paired with the
 | ||||||
|  |     /// [`consume`] method to function properly. When calling this
 | ||||||
|  |     /// method, none of the contents will be "read" in the sense that later
 | ||||||
|  |     /// calling [`poll_read`] may return the same contents. As such, [`consume`] must
 | ||||||
|  |     /// be called with the number of bytes that are consumed from this buffer to
 | ||||||
|  |     /// ensure that the bytes are never returned twice.
 | ||||||
|  |     ///
 | ||||||
|  |     /// [`poll_read`]: AsyncBufRead::poll_read
 | ||||||
|  |     /// [`consume`]: AsyncBufRead::consume
 | ||||||
|  |     ///
 | ||||||
|  |     /// An empty buffer returned indicates that the stream has reached EOF.
 | ||||||
|  |     ///
 | ||||||
|  |     /// # Implementation
 | ||||||
|  |     ///
 | ||||||
|  |     /// This function may not return errors of kind `WouldBlock` or
 | ||||||
|  |     /// `Interrupted`.  Implementations must convert `WouldBlock` into
 | ||||||
|  |     /// `Poll::Pending` and either internally retry or convert
 | ||||||
|  |     /// `Interrupted` into another error kind.
 | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>; | ||||||
|  | 
 | ||||||
|  |     /// Tells this buffer that `amt` bytes have been consumed from the buffer,
 | ||||||
|  |     /// so they should no longer be returned in calls to [`poll_read`].
 | ||||||
|  |     ///
 | ||||||
|  |     /// This function is a lower-level call. It needs to be paired with the
 | ||||||
|  |     /// [`poll_fill_buf`] method to function properly. This function does
 | ||||||
|  |     /// not perform any I/O, it simply informs this object that some amount of
 | ||||||
|  |     /// its buffer, returned from [`poll_fill_buf`], has been consumed and should
 | ||||||
|  |     /// no longer be returned. As such, this function may do odd things if
 | ||||||
|  |     /// [`poll_fill_buf`] isn't called before calling it.
 | ||||||
|  |     ///
 | ||||||
|  |     /// The `amt` must be `<=` the number of bytes in the buffer returned by
 | ||||||
|  |     /// [`poll_fill_buf`].
 | ||||||
|  |     ///
 | ||||||
|  |     /// [`poll_read`]: AsyncBufRead::poll_read
 | ||||||
|  |     /// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
 | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Write bytes asynchronously.
 | ||||||
|  | ///
 | ||||||
|  | /// This trait is analogous to the `core::io::Write` trait, but integrates
 | ||||||
|  | /// with the asynchronous task system. In particular, the `poll_write`
 | ||||||
|  | /// method, unlike `Write::write`, will automatically queue the current task
 | ||||||
|  | /// for wakeup and return if the writer cannot take more data, rather than blocking
 | ||||||
|  | /// the calling thread.
 | ||||||
|  | pub trait AsyncWrite { | ||||||
|  |     /// Attempt to write bytes from `buf` into the object.
 | ||||||
|  |     ///
 | ||||||
|  |     /// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
 | ||||||
|  |     ///
 | ||||||
|  |     /// If the object is not ready for writing, the method returns
 | ||||||
|  |     /// `Poll::Pending` and arranges for the current task (via
 | ||||||
|  |     /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
 | ||||||
|  |     /// writable or is closed.
 | ||||||
|  |     ///
 | ||||||
|  |     /// # Implementation
 | ||||||
|  |     ///
 | ||||||
|  |     /// This function may not return errors of kind `WouldBlock` or
 | ||||||
|  |     /// `Interrupted`.  Implementations must convert `WouldBlock` into
 | ||||||
|  |     /// `Poll::Pending` and either internally retry or convert
 | ||||||
|  |     /// `Interrupted` into another error kind.
 | ||||||
|  |     ///
 | ||||||
|  |     /// `poll_write` must try to make progress by flushing the underlying object if
 | ||||||
|  |     /// that is the only way the underlying object can become writable again.
 | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! defer_async_read { | ||||||
|  |     () => { | ||||||
|  |         fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |             Pin::new(&mut **self.get_mut()).poll_fill_buf(cx) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn consume(mut self: Pin<&mut Self>, amt: usize) { | ||||||
|  |             Pin::new(&mut **self).consume(amt) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "alloc")] | ||||||
|  | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> { | ||||||
|  |     defer_async_read!(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T { | ||||||
|  |     defer_async_read!(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<P> AsyncBufRead for Pin<P> | ||||||
|  | where | ||||||
|  |     P: DerefMut + Unpin, | ||||||
|  |     P::Target: AsyncBufRead, | ||||||
|  | { | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |         self.get_mut().as_mut().poll_fill_buf(cx) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize) { | ||||||
|  |         self.get_mut().as_mut().consume(amt) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! deref_async_write { | ||||||
|  |     () => { | ||||||
|  |         fn poll_write( | ||||||
|  |             mut self: Pin<&mut Self>, | ||||||
|  |             cx: &mut Context<'_>, | ||||||
|  |             buf: &[u8], | ||||||
|  |         ) -> Poll<Result<usize>> { | ||||||
|  |             Pin::new(&mut **self).poll_write(cx, buf) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "alloc")] | ||||||
|  | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> { | ||||||
|  |     deref_async_write!(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T { | ||||||
|  |     deref_async_write!(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<P> AsyncWrite for Pin<P> | ||||||
|  | where | ||||||
|  |     P: DerefMut + Unpin, | ||||||
|  |     P::Target: AsyncWrite, | ||||||
|  | { | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||||||
|  |         self.get_mut().as_mut().poll_write(cx, buf) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | pub struct FromStdIo<T>(T); | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl<T> FromStdIo<T> { | ||||||
|  |     pub fn new(inner: T) -> Self { | ||||||
|  |         Self(inner) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> { | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |         let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||||||
|  |         unsafe { Pin::new_unchecked(inner) } | ||||||
|  |             .poll_fill_buf(cx) | ||||||
|  |             .map_err(|e| e.into()) | ||||||
|  |     } | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize) { | ||||||
|  |         let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||||||
|  |         unsafe { Pin::new_unchecked(inner) }.consume(amt) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> { | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||||||
|  |         let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||||||
|  |         unsafe { Pin::new_unchecked(inner) } | ||||||
|  |             .poll_write(cx, buf) | ||||||
|  |             .map_err(|e| e.into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										80
									
								
								embassy/src/io/util/copy_buf.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								embassy/src/io/util/copy_buf.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | use core::future::Future; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use core::task::{Context, Poll}; | ||||||
|  | use futures::ready; | ||||||
|  | use pin_project::pin_project; | ||||||
|  | 
 | ||||||
|  | use crate::io::{AsyncBufRead, AsyncWrite, Error, Result}; | ||||||
|  | 
 | ||||||
|  | /// Creates a future which copies all the bytes from one object to another.
 | ||||||
|  | ///
 | ||||||
|  | /// The returned future will copy all the bytes read from this `AsyncBufRead` into the
 | ||||||
|  | /// `writer` specified. This future will only complete once the `reader` has hit
 | ||||||
|  | /// EOF and all bytes have been written to and flushed from the `writer`
 | ||||||
|  | /// provided.
 | ||||||
|  | ///
 | ||||||
|  | /// On success the number of bytes is returned.
 | ||||||
|  | ///
 | ||||||
|  | /// # Examples
 | ||||||
|  | ///
 | ||||||
|  | /// ```
 | ||||||
|  | /// # futures::executor::block_on(async {
 | ||||||
|  | /// use futures::io::{self, AsyncWriteExt, Cursor};
 | ||||||
|  | ///
 | ||||||
|  | /// let reader = Cursor::new([1, 2, 3, 4]);
 | ||||||
|  | /// let mut writer = Cursor::new(vec![0u8; 5]);
 | ||||||
|  | ///
 | ||||||
|  | /// let bytes = io::copy_buf(reader, &mut writer).await?;
 | ||||||
|  | /// writer.close().await?;
 | ||||||
|  | ///
 | ||||||
|  | /// assert_eq!(bytes, 4);
 | ||||||
|  | /// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
 | ||||||
|  | /// # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
 | ||||||
|  | /// ```
 | ||||||
|  | pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W> | ||||||
|  | where | ||||||
|  |     R: AsyncBufRead, | ||||||
|  |     W: AsyncWrite + Unpin + ?Sized, | ||||||
|  | { | ||||||
|  |     CopyBuf { | ||||||
|  |         reader, | ||||||
|  |         writer, | ||||||
|  |         amt: 0, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Future for the [`copy_buf()`] function.
 | ||||||
|  | #[pin_project] | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct CopyBuf<'a, R, W: ?Sized> { | ||||||
|  |     #[pin] | ||||||
|  |     reader: R, | ||||||
|  |     writer: &'a mut W, | ||||||
|  |     amt: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R, W> Future for CopyBuf<'_, R, W> | ||||||
|  | where | ||||||
|  |     R: AsyncBufRead, | ||||||
|  |     W: AsyncWrite + Unpin + ?Sized, | ||||||
|  | { | ||||||
|  |     type Output = Result<usize>; | ||||||
|  | 
 | ||||||
|  |     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let mut this = self.project(); | ||||||
|  |         loop { | ||||||
|  |             let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?; | ||||||
|  |             if buffer.is_empty() { | ||||||
|  |                 return Poll::Ready(Ok(*this.amt)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?; | ||||||
|  |             if i == 0 { | ||||||
|  |                 return Poll::Ready(Err(Error::WriteZero.into())); | ||||||
|  |             } | ||||||
|  |             *this.amt += i; | ||||||
|  |             this.reader.as_mut().consume(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										145
									
								
								embassy/src/io/util/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								embassy/src/io/util/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | |||||||
|  | use core::cmp::min; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use core::task::{Context, Poll}; | ||||||
|  | use futures::ready; | ||||||
|  | 
 | ||||||
|  | mod read; | ||||||
|  | pub use self::read::Read; | ||||||
|  | 
 | ||||||
|  | mod read_buf; | ||||||
|  | pub use self::read_buf::ReadBuf; | ||||||
|  | 
 | ||||||
|  | mod read_byte; | ||||||
|  | pub use self::read_byte::ReadByte; | ||||||
|  | 
 | ||||||
|  | mod read_exact; | ||||||
|  | pub use self::read_exact::ReadExact; | ||||||
|  | 
 | ||||||
|  | mod read_while; | ||||||
|  | pub use self::read_while::ReadWhile; | ||||||
|  | 
 | ||||||
|  | mod read_to_end; | ||||||
|  | pub use self::read_to_end::ReadToEnd; | ||||||
|  | 
 | ||||||
|  | mod skip_while; | ||||||
|  | pub use self::skip_while::SkipWhile; | ||||||
|  | 
 | ||||||
|  | mod write; | ||||||
|  | pub use self::write::Write; | ||||||
|  | 
 | ||||||
|  | mod write_all; | ||||||
|  | pub use self::write_all::WriteAll; | ||||||
|  | 
 | ||||||
|  | mod write_byte; | ||||||
|  | pub use self::write_byte::WriteByte; | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "alloc")] | ||||||
|  | mod split; | ||||||
|  | #[cfg(feature = "alloc")] | ||||||
|  | pub use self::split::{split, ReadHalf, WriteHalf}; | ||||||
|  | 
 | ||||||
|  | mod copy_buf; | ||||||
|  | pub use self::copy_buf::{copy_buf, CopyBuf}; | ||||||
|  | 
 | ||||||
|  | use super::error::Result; | ||||||
|  | use super::traits::{AsyncBufRead, AsyncWrite}; | ||||||
|  | 
 | ||||||
|  | pub trait AsyncBufReadExt: AsyncBufRead { | ||||||
|  |     fn poll_read( | ||||||
|  |         mut self: Pin<&mut Self>, | ||||||
|  |         cx: &mut Context<'_>, | ||||||
|  |         buf: &mut [u8], | ||||||
|  |     ) -> Poll<Result<usize>> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         let mut this = &mut *self; | ||||||
|  |         let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?; | ||||||
|  |         let n = min(buf.len(), rbuf.len()); | ||||||
|  |         buf[..n].copy_from_slice(&rbuf[..n]); | ||||||
|  |         Pin::new(&mut this).consume(n); | ||||||
|  |         Poll::Ready(Ok(n)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_while<'a, F: Fn(u8) -> bool>( | ||||||
|  |         &'a mut self, | ||||||
|  |         buf: &'a mut [u8], | ||||||
|  |         f: F, | ||||||
|  |     ) -> ReadWhile<'a, Self, F> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         ReadWhile::new(self, f, buf) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn skip_while<'a, F: Fn(u8) -> bool>(&'a mut self, f: F) -> SkipWhile<'a, Self, F> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         SkipWhile::new(self, f) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         Read::new(self, buf) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_buf<'a>(&'a mut self) -> ReadBuf<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         ReadBuf::new(self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_byte<'a>(&'a mut self) -> ReadByte<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         ReadByte::new(self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         ReadExact::new(self, buf) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         ReadToEnd::new(self, buf) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {} | ||||||
|  | 
 | ||||||
|  | pub async fn read_line<R: AsyncBufRead + Unpin>(r: &mut R, buf: &mut [u8]) -> Result<usize> { | ||||||
|  |     r.skip_while(|b| b == b'\r' || b == b'\n').await?; | ||||||
|  |     let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?; | ||||||
|  |     r.skip_while(|b| b == b'\r').await?; | ||||||
|  |     //assert_eq!(b'\n', r.read_byte().await?);
 | ||||||
|  |     r.read_byte().await?; | ||||||
|  |     Ok(n) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait AsyncWriteExt: AsyncWrite { | ||||||
|  |     fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         WriteAll::new(self, buf) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn write_byte<'a>(&'a mut self, byte: u8) -> WriteByte<'a, Self> | ||||||
|  |     where | ||||||
|  |         Self: Unpin, | ||||||
|  |     { | ||||||
|  |         WriteByte::new(self, byte) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: AsyncWrite + ?Sized> AsyncWriteExt for R {} | ||||||
							
								
								
									
										39
									
								
								embassy/src/io/util/read.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								embassy/src/io/util/read.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | use super::super::error::{Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | use core::cmp::min; | ||||||
|  | 
 | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | /// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct Read<'a, R: ?Sized> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  |     buf: &'a mut [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { | ||||||
|  |         Read { reader, buf } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: AsyncBufRead + ?Sized + Unpin> Future for Read<'_, R> { | ||||||
|  |     type Output = Result<usize>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let this = &mut *self; | ||||||
|  |         let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?; | ||||||
|  | 
 | ||||||
|  |         let n = min(this.buf.len(), buf.len()); | ||||||
|  |         this.buf[..n].copy_from_slice(&buf[..n]); | ||||||
|  |         Pin::new(&mut this.reader).consume(n); | ||||||
|  |         Poll::Ready(Ok(n)) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								embassy/src/io/util/read_buf.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								embassy/src/io/util/read_buf.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | use super::super::error::{Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | pub struct ReadBuf<'a, R: ?Sized> { | ||||||
|  |     reader: Option<&'a mut R>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R) -> Self { | ||||||
|  |         ReadBuf { | ||||||
|  |             reader: Some(reader), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> { | ||||||
|  |     type Output = Result<&'a [u8]>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let this = &mut *self; | ||||||
|  | 
 | ||||||
|  |         let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?; | ||||||
|  |         let buf: &'a [u8] = unsafe { core::mem::transmute(buf) }; | ||||||
|  |         this.reader = None; | ||||||
|  |         Poll::Ready(Ok(buf)) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								embassy/src/io/util/read_byte.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								embassy/src/io/util/read_byte.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::{Error, Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | pub struct ReadByte<'a, R: ?Sized> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R) -> Self { | ||||||
|  |         Self { reader } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> { | ||||||
|  |     type Output = Result<u8>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let Self { reader } = &mut *self; | ||||||
|  |         let mut reader = Pin::new(reader); | ||||||
|  |         let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; | ||||||
|  |         if rbuf.len() == 0 { | ||||||
|  |             return Poll::Ready(Err(Error::UnexpectedEof)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let r = rbuf[0]; | ||||||
|  |         reader.as_mut().consume(1); | ||||||
|  |         Poll::Ready(Ok(r)) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								embassy/src/io/util/read_exact.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								embassy/src/io/util/read_exact.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | use super::super::error::{Error, Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | use core::cmp::min; | ||||||
|  | use core::mem; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | /// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct ReadExact<'a, R: ?Sized> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  |     buf: &'a mut [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { | ||||||
|  |         ReadExact { reader, buf } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadExact<'_, R> { | ||||||
|  |     type Output = Result<()>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let this = &mut *self; | ||||||
|  |         while !this.buf.is_empty() { | ||||||
|  |             let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?; | ||||||
|  |             if buf.len() == 0 { | ||||||
|  |                 return Poll::Ready(Err(Error::UnexpectedEof)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let n = min(this.buf.len(), buf.len()); | ||||||
|  |             this.buf[..n].copy_from_slice(&buf[..n]); | ||||||
|  |             Pin::new(&mut this.reader).consume(n); | ||||||
|  |             { | ||||||
|  |                 let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n); | ||||||
|  |                 this.buf = rest; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Poll::Ready(Ok(())) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								embassy/src/io/util/read_to_end.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								embassy/src/io/util/read_to_end.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | use core::cmp::min; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::{Error, Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | pub struct ReadToEnd<'a, R: ?Sized> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  |     buf: &'a mut [u8], | ||||||
|  |     n: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self { | ||||||
|  |         Self { reader, buf, n: 0 } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> { | ||||||
|  |     type Output = Result<usize>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let Self { reader, buf, n } = &mut *self; | ||||||
|  |         let mut reader = Pin::new(reader); | ||||||
|  |         loop { | ||||||
|  |             let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; | ||||||
|  |             if rbuf.len() == 0 { | ||||||
|  |                 return Poll::Ready(Ok(*n)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if *n == buf.len() { | ||||||
|  |                 return Poll::Ready(Err(Error::Truncated)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // truncate data if it doesn't fit in buf
 | ||||||
|  |             let p = min(rbuf.len(), buf.len() - *n); | ||||||
|  |             buf[*n..*n + p].copy_from_slice(&rbuf[..p]); | ||||||
|  |             *n += p; | ||||||
|  | 
 | ||||||
|  |             reader.as_mut().consume(p); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								embassy/src/io/util/read_while.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								embassy/src/io/util/read_while.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | use core::cmp::min; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::{Error, Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | pub struct ReadWhile<'a, R: ?Sized, F> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  |     buf: &'a mut [u8], | ||||||
|  |     n: usize, | ||||||
|  |     f: F, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self { | ||||||
|  |         Self { | ||||||
|  |             reader, | ||||||
|  |             f, | ||||||
|  |             buf, | ||||||
|  |             n: 0, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> { | ||||||
|  |     type Output = Result<usize>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let Self { reader, f, buf, n } = &mut *self; | ||||||
|  |         let mut reader = Pin::new(reader); | ||||||
|  |         loop { | ||||||
|  |             let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?; | ||||||
|  |             if rbuf.len() == 0 { | ||||||
|  |                 return Poll::Ready(Err(Error::UnexpectedEof)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let (p, done) = match rbuf.iter().position(|&b| !f(b)) { | ||||||
|  |                 Some(p) => (p, true), | ||||||
|  |                 None => (rbuf.len(), false), | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // truncate data if it doesn't fit in buf
 | ||||||
|  |             let p2 = min(p, buf.len() - *n); | ||||||
|  |             buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]); | ||||||
|  |             *n += p2; | ||||||
|  | 
 | ||||||
|  |             // consume it all, even if it doesn't fit.
 | ||||||
|  |             // Otherwise we can deadlock because we never read to the ending char
 | ||||||
|  |             reader.as_mut().consume(p); | ||||||
|  | 
 | ||||||
|  |             if done { | ||||||
|  |                 return Poll::Ready(Ok(*n)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								embassy/src/io/util/skip_while.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								embassy/src/io/util/skip_while.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | use core::iter::Iterator; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::{Error, Result}; | ||||||
|  | use super::super::traits::AsyncBufRead; | ||||||
|  | 
 | ||||||
|  | pub struct SkipWhile<'a, R: ?Sized, F> { | ||||||
|  |     reader: &'a mut R, | ||||||
|  |     f: F, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> { | ||||||
|  |     pub(super) fn new(reader: &'a mut R, f: F) -> Self { | ||||||
|  |         Self { reader, f } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> { | ||||||
|  |     type Output = Result<()>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||||||
|  |         let Self { reader, f } = &mut *self; | ||||||
|  |         let mut reader = Pin::new(reader); | ||||||
|  |         loop { | ||||||
|  |             let buf = ready!(reader.as_mut().poll_fill_buf(cx))?; | ||||||
|  |             if buf.len() == 0 { | ||||||
|  |                 return Poll::Ready(Err(Error::UnexpectedEof)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let (p, done) = match buf.iter().position(|b| !f(*b)) { | ||||||
|  |                 Some(p) => (p, true), | ||||||
|  |                 None => (buf.len(), false), | ||||||
|  |             }; | ||||||
|  |             reader.as_mut().consume(p); | ||||||
|  |             if done { | ||||||
|  |                 return Poll::Ready(Ok(())); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								embassy/src/io/util/split.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								embassy/src/io/util/split.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | use alloc::rc::Rc; | ||||||
|  | use core::cell::UnsafeCell; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::Result; | ||||||
|  | use super::super::traits::{AsyncBufRead, AsyncWrite}; | ||||||
|  | 
 | ||||||
|  | /// The readable half of an object returned from `AsyncBufRead::split`.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ReadHalf<T> { | ||||||
|  |     handle: Rc<UnsafeCell<T>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// The writable half of an object returned from `AsyncBufRead::split`.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct WriteHalf<T> { | ||||||
|  |     handle: Rc<UnsafeCell<T>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: AsyncBufRead + Unpin> AsyncBufRead for ReadHalf<T> { | ||||||
|  |     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||||||
|  |         Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn consume(self: Pin<&mut Self>, amt: usize) { | ||||||
|  |         Pin::new(unsafe { &mut *self.handle.get() }).consume(amt) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T: AsyncWrite + Unpin> AsyncWrite for WriteHalf<T> { | ||||||
|  |     fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||||||
|  |         Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn split<T: AsyncBufRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) { | ||||||
|  |     let c = Rc::new(UnsafeCell::new(t)); | ||||||
|  |     (ReadHalf { handle: c.clone() }, WriteHalf { handle: c }) | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								embassy/src/io/util/write.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								embassy/src/io/util/write.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::Result; | ||||||
|  | use super::super::traits::AsyncWrite; | ||||||
|  | 
 | ||||||
|  | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct Write<'a, W: ?Sized> { | ||||||
|  |     writer: &'a mut W, | ||||||
|  |     buf: &'a [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> { | ||||||
|  |     pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self { | ||||||
|  |         Write { writer, buf } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: AsyncWrite + ?Sized + Unpin> Future for Write<'_, W> { | ||||||
|  |     type Output = Result<usize>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<usize>> { | ||||||
|  |         let this = &mut *self; | ||||||
|  |         let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?; | ||||||
|  |         Poll::Ready(Ok(n)) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								embassy/src/io/util/write_all.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								embassy/src/io/util/write_all.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | use core::mem; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::Result; | ||||||
|  | use super::super::traits::AsyncWrite; | ||||||
|  | 
 | ||||||
|  | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct WriteAll<'a, W: ?Sized> { | ||||||
|  |     writer: &'a mut W, | ||||||
|  |     buf: &'a [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> { | ||||||
|  |     pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self { | ||||||
|  |         WriteAll { writer, buf } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteAll<'_, W> { | ||||||
|  |     type Output = Result<()>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> { | ||||||
|  |         let this = &mut *self; | ||||||
|  |         while !this.buf.is_empty() { | ||||||
|  |             let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?; | ||||||
|  |             { | ||||||
|  |                 let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n); | ||||||
|  |                 this.buf = rest; | ||||||
|  |             } | ||||||
|  |             if n == 0 { | ||||||
|  |                 panic!(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Poll::Ready(Ok(())) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								embassy/src/io/util/write_byte.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								embassy/src/io/util/write_byte.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | use core::pin::Pin; | ||||||
|  | use futures::future::Future; | ||||||
|  | use futures::ready; | ||||||
|  | use futures::task::{Context, Poll}; | ||||||
|  | 
 | ||||||
|  | use super::super::error::Result; | ||||||
|  | use super::super::traits::AsyncWrite; | ||||||
|  | 
 | ||||||
|  | /// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||||||
|  | pub struct WriteByte<'a, W: ?Sized> { | ||||||
|  |     writer: &'a mut W, | ||||||
|  |     byte: u8, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {} | ||||||
|  | 
 | ||||||
|  | impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> { | ||||||
|  |     pub(super) fn new(writer: &'a mut W, byte: u8) -> Self { | ||||||
|  |         WriteByte { writer, byte } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteByte<'_, W> { | ||||||
|  |     type Output = Result<()>; | ||||||
|  | 
 | ||||||
|  |     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> { | ||||||
|  |         let this = &mut *self; | ||||||
|  |         let buf = [this.byte; 1]; | ||||||
|  |         let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?; | ||||||
|  |         if n == 0 { | ||||||
|  |             panic!(); | ||||||
|  |         } | ||||||
|  |         assert!(n == 1); | ||||||
|  | 
 | ||||||
|  |         Poll::Ready(Ok(())) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								embassy/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								embassy/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![feature(slice_fill)] | ||||||
|  | #![feature(generic_associated_types)] | ||||||
|  | #![feature(const_fn)] | ||||||
|  | 
 | ||||||
|  | pub mod flash; | ||||||
|  | pub mod util; | ||||||
|  | pub mod io; | ||||||
							
								
								
									
										21
									
								
								embassy/src/util/drop_bomb.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								embassy/src/util/drop_bomb.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | use core::mem; | ||||||
|  | 
 | ||||||
|  | pub struct DropBomb { | ||||||
|  |     _private: (), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DropBomb { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         Self { _private: () } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn defuse(self) { | ||||||
|  |         mem::forget(self) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for DropBomb { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         depanic!("boom") | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								embassy/src/util/macros.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								embassy/src/util/macros.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | #![macro_use] | ||||||
|  | 
 | ||||||
|  | macro_rules! depanic { | ||||||
|  |     ($( $i:expr ),*) => { | ||||||
|  |         { | ||||||
|  |             defmt::error!($( $i ),*); | ||||||
|  |             panic!(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! deassert { | ||||||
|  |     ($cond:expr) => { | ||||||
|  |         deassert!($cond, "assertion failed"); | ||||||
|  |     }; | ||||||
|  |     ($cond:expr, $msg:literal) => { | ||||||
|  |         { | ||||||
|  |             if !$cond { | ||||||
|  |                 defmt::error!($msg); | ||||||
|  |                 panic!(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ($cond:expr, $msg:literal, $( $i:expr ),*) => { | ||||||
|  |         { | ||||||
|  |             if !$cond { | ||||||
|  |                 defmt::error!($msg, $( $i ),*); | ||||||
|  |                 panic!(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								embassy/src/util/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								embassy/src/util/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | #![macro_use] | ||||||
|  | 
 | ||||||
|  | mod macros; | ||||||
|  | 
 | ||||||
|  | mod signal; | ||||||
|  | pub use signal::*; | ||||||
|  | mod portal; | ||||||
|  | pub use portal::*; | ||||||
|  | mod waker_store; | ||||||
|  | pub use waker_store::*; | ||||||
|  | mod drop_bomb; | ||||||
|  | pub use drop_bomb::*; | ||||||
|  | 
 | ||||||
|  | use defmt::{warn, error}; | ||||||
|  | 
 | ||||||
|  | pub trait Dewrap<T> { | ||||||
|  |     /// dewrap = defmt unwrap
 | ||||||
|  |     fn dewrap(self) -> T; | ||||||
|  | 
 | ||||||
|  |     /// dexpect = defmt expect
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T; | ||||||
|  | 
 | ||||||
|  |     fn dewarn<M: defmt::Format>(self, msg: M) -> Self; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> Dewrap<T> for Option<T> { | ||||||
|  |     fn dewrap(self) -> T { | ||||||
|  |         match self { | ||||||
|  |             Some(t) => t, | ||||||
|  |             None => depanic!("unwrap failed: enum is none"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T { | ||||||
|  |         match self { | ||||||
|  |             Some(t) => t, | ||||||
|  |             None => depanic!("unexpected None: {:?}", msg), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dewarn<M: defmt::Format>(self, msg: M) -> Self { | ||||||
|  |         if self.is_none() { | ||||||
|  |             warn!("{:?} is none", msg); | ||||||
|  |         } | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> { | ||||||
|  |     fn dewrap(self) -> T { | ||||||
|  |         match self { | ||||||
|  |             Ok(t) => t, | ||||||
|  |             Err(e) => depanic!("unwrap failed: {:?}", e), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T { | ||||||
|  |         match self { | ||||||
|  |             Ok(t) => t, | ||||||
|  |             Err(e) => depanic!("unexpected error: {:?}: {:?}", msg, e), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dewarn<M: defmt::Format>(self, msg: M) -> Self { | ||||||
|  |         if let Err(e) = &self { | ||||||
|  |             warn!("{:?} err: {:?}", msg, e); | ||||||
|  |         } | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										125
									
								
								embassy/src/util/portal.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								embassy/src/util/portal.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | |||||||
|  | use core::cell::UnsafeCell; | ||||||
|  | use core::future::Future; | ||||||
|  | use core::mem; | ||||||
|  | use core::mem::MaybeUninit; | ||||||
|  | 
 | ||||||
|  | use crate::util::*; | ||||||
|  | 
 | ||||||
|  | /// Utility to call a closure across tasks.
 | ||||||
|  | pub struct Portal<T> { | ||||||
|  |     state: UnsafeCell<State<T>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum State<T> { | ||||||
|  |     None, | ||||||
|  |     Running, | ||||||
|  |     Waiting(*mut dyn FnMut(T)), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> Portal<T> { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             state: UnsafeCell::new(State::None), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn call(&self, val: T) { | ||||||
|  |         unsafe { | ||||||
|  |             match *self.state.get() { | ||||||
|  |                 State::None => {} | ||||||
|  |                 State::Running => depanic!("Portall::call() called reentrantly"), | ||||||
|  |                 State::Waiting(func) => (*func)(val), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn wait_once<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a | ||||||
|  |     where | ||||||
|  |         F: FnMut(T) -> R + 'a, | ||||||
|  |     { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             let signal = Signal::new(); | ||||||
|  |             let mut result: MaybeUninit<R> = MaybeUninit::uninit(); | ||||||
|  |             let mut call_func = |val: T| { | ||||||
|  |                 unsafe { | ||||||
|  |                     let state = &mut *self.state.get(); | ||||||
|  |                     *state = State::None; | ||||||
|  |                     result.as_mut_ptr().write(func(val)) | ||||||
|  |                 }; | ||||||
|  |                 signal.signal(()); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             let func_ptr: *mut dyn FnMut(T) = &mut call_func as _; | ||||||
|  |             let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) }; | ||||||
|  | 
 | ||||||
|  |             unsafe { | ||||||
|  |                 let state = &mut *self.state.get(); | ||||||
|  |                 match state { | ||||||
|  |                     State::None => {} | ||||||
|  |                     _ => depanic!("Multiple tasks waiting on same portal"), | ||||||
|  |                 } | ||||||
|  |                 *state = State::Waiting(func_ptr); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             signal.wait().await; | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             unsafe { result.assume_init() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn wait_many<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a | ||||||
|  |     where | ||||||
|  |         F: FnMut(T) -> Option<R> + 'a, | ||||||
|  |     { | ||||||
|  |         async move { | ||||||
|  |             let bomb = DropBomb::new(); | ||||||
|  | 
 | ||||||
|  |             let signal = Signal::new(); | ||||||
|  |             let mut result: MaybeUninit<R> = MaybeUninit::uninit(); | ||||||
|  |             let mut call_func = |val: T| { | ||||||
|  |                 unsafe { | ||||||
|  |                     let state = &mut *self.state.get(); | ||||||
|  | 
 | ||||||
|  |                     let func_ptr = match *state { | ||||||
|  |                         State::Waiting(p) => p, | ||||||
|  |                         _ => unreachable!(), | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     // Set state to Running while running the function to avoid reentrancy.
 | ||||||
|  |                     *state = State::Running; | ||||||
|  | 
 | ||||||
|  |                     *state = match func(val) { | ||||||
|  |                         None => State::Waiting(func_ptr), | ||||||
|  |                         Some(res) => { | ||||||
|  |                             result.as_mut_ptr().write(res); | ||||||
|  |                             signal.signal(()); | ||||||
|  |                             State::None | ||||||
|  |                         } | ||||||
|  |                     }; | ||||||
|  |                 }; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             let func_ptr: *mut dyn FnMut(T) = &mut call_func as _; | ||||||
|  |             let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) }; | ||||||
|  | 
 | ||||||
|  |             unsafe { | ||||||
|  |                 let state = &mut *self.state.get(); | ||||||
|  |                 match *state { | ||||||
|  |                     State::None => {} | ||||||
|  |                     _ => depanic!("Multiple tasks waiting on same portal"), | ||||||
|  |                 } | ||||||
|  |                 *state = State::Waiting(func_ptr); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             signal.wait().await; | ||||||
|  | 
 | ||||||
|  |             bomb.defuse(); | ||||||
|  | 
 | ||||||
|  |             unsafe { result.assume_init() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								embassy/src/util/signal.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								embassy/src/util/signal.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | use core::cell::UnsafeCell; | ||||||
|  | use core::future::Future; | ||||||
|  | use core::mem; | ||||||
|  | use core::pin::Pin; | ||||||
|  | use core::task::{Context, Poll, Waker}; | ||||||
|  | 
 | ||||||
|  | pub struct Signal<T> { | ||||||
|  |     state: UnsafeCell<State<T>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum State<T> { | ||||||
|  |     None, | ||||||
|  |     Waiting(Waker), | ||||||
|  |     Signaled(T), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsafe impl<T: Send> Send for Signal<T> {} | ||||||
|  | unsafe impl<T: Send> Sync for Signal<T> {} | ||||||
|  | 
 | ||||||
|  | impl<T: Send> Signal<T> { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         Self { | ||||||
|  |             state: UnsafeCell::new(State::None), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn signal(&self, val: T) { | ||||||
|  |         unsafe { | ||||||
|  |             cortex_m::interrupt::free(|_| { | ||||||
|  |                 let state = &mut *self.state.get(); | ||||||
|  |                 match mem::replace(state, State::Signaled(val)) { | ||||||
|  |                     State::Waiting(waker) => waker.wake(), | ||||||
|  |                     _ => {} | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn wait<'a>(&'a self) -> impl Future<Output = T> + 'a { | ||||||
|  |         WaitFuture { signal: self } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct WaitFuture<'a, T> { | ||||||
|  |     signal: &'a Signal<T>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, T: Send> Future for WaitFuture<'a, T> { | ||||||
|  |     type Output = T; | ||||||
|  | 
 | ||||||
|  |     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { | ||||||
|  |         unsafe { | ||||||
|  |             cortex_m::interrupt::free(|_| { | ||||||
|  |                 let state = &mut *self.signal.state.get(); | ||||||
|  |                 match state { | ||||||
|  |                     State::None => { | ||||||
|  |                         *state = State::Waiting(cx.waker().clone()); | ||||||
|  |                         Poll::Pending | ||||||
|  |                     } | ||||||
|  |                     State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, | ||||||
|  |                     State::Waiting(_) => depanic!("waker overflow"), | ||||||
|  |                     State::Signaled(_) => match mem::replace(state, State::None) { | ||||||
|  |                         State::Signaled(res) => Poll::Ready(res), | ||||||
|  |                         _ => unreachable!(), | ||||||
|  |                     }, | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								embassy/src/util/waker_store.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								embassy/src/util/waker_store.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | use core::task::Waker; | ||||||
|  | 
 | ||||||
|  | pub struct WakerStore { | ||||||
|  |     waker: Option<Waker>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl WakerStore { | ||||||
|  |     pub const fn new() -> Self { | ||||||
|  |         Self { waker: None } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn store(&mut self, w: &Waker) { | ||||||
|  |         match self.waker { | ||||||
|  |             Some(ref w2) if (w2.will_wake(w)) => {} | ||||||
|  |             Some(_) => panic!("Waker overflow"), | ||||||
|  |             None => self.waker = Some(w.clone()), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn wake(&mut self) { | ||||||
|  |         self.waker.take().map(|w| w.wake()); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								examples/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | [package] | ||||||
|  | authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"] | ||||||
|  | edition = "2018" | ||||||
|  | name = "embassy-examples" | ||||||
|  | version = "0.1.0" | ||||||
|  | 
 | ||||||
|  | [features] | ||||||
|  | default = [ | ||||||
|  |     "defmt-default", | ||||||
|  | ] | ||||||
|  | defmt-default = [] | ||||||
|  | defmt-trace = [] | ||||||
|  | defmt-debug = [] | ||||||
|  | defmt-info = [] | ||||||
|  | defmt-warn = [] | ||||||
|  | defmt-error = [] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | cortex-m        = { version = "0.6.3" } | ||||||
|  | cortex-m-rt = "0.6.12" | ||||||
|  | defmt = "0.1.0" | ||||||
|  | embedded-hal    = { version = "0.2.4" } | ||||||
|  | defmt-rtt = "0.1.0" | ||||||
|  | panic-probe = "0.1.0" | ||||||
|  | nrf52840-hal    = { version = "0.11.0" } | ||||||
|  | embassy = { version = "0.1.0", path = "../embassy" } | ||||||
|  | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "nrf52840"] } | ||||||
|  | static-executor  = { version = "0.1.0", features=["defmt"]} | ||||||
|  | static-executor-cortex-m  = { version = "0.1.0" } | ||||||
|  | futures = { version = "0.3.5", default-features = false } | ||||||
							
								
								
									
										31
									
								
								examples/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/build.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | //! This build script copies the `memory.x` file from the crate root into
 | ||||||
|  | //! a directory where the linker can always find it at build time.
 | ||||||
|  | //! For many projects this is optional, as the linker always searches the
 | ||||||
|  | //! project root directory -- wherever `Cargo.toml` is. However, if you
 | ||||||
|  | //! are using a workspace or have a more complicated build setup, this
 | ||||||
|  | //! build script becomes required. Additionally, by requesting that
 | ||||||
|  | //! Cargo re-run the build script whenever `memory.x` is changed,
 | ||||||
|  | //! updating `memory.x` ensures a rebuild of the application with the
 | ||||||
|  | //! new memory settings.
 | ||||||
|  | 
 | ||||||
|  | use std::env; | ||||||
|  | use std::fs::File; | ||||||
|  | use std::io::Write; | ||||||
|  | use std::path::PathBuf; | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     // Put `memory.x` in our output directory and ensure it's
 | ||||||
|  |     // on the linker search path.
 | ||||||
|  |     let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||||||
|  |     File::create(out.join("memory.x")) | ||||||
|  |         .unwrap() | ||||||
|  |         .write_all(include_bytes!("memory.x")) | ||||||
|  |         .unwrap(); | ||||||
|  |     println!("cargo:rustc-link-search={}", out.display()); | ||||||
|  | 
 | ||||||
|  |     // By default, Cargo will re-run a build script whenever
 | ||||||
|  |     // any file in the project changes. By specifying `memory.x`
 | ||||||
|  |     // here, we ensure the build script is only re-run when
 | ||||||
|  |     // `memory.x` is changed.
 | ||||||
|  |     println!("cargo:rerun-if-changed=memory.x"); | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								examples/memory.x
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/memory.x
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | MEMORY | ||||||
|  | { | ||||||
|  |   /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||||||
|  |   /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ | ||||||
|  |   FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | ||||||
|  |   RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||||||
|  | } | ||||||
							
								
								
									
										123
									
								
								examples/src/bin/qspi.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								examples/src/bin/qspi.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![no_main] | ||||||
|  | #![feature(type_alias_impl_trait)] | ||||||
|  | 
 | ||||||
|  | #[path = "../example_common.rs"] | ||||||
|  | mod example_common; | ||||||
|  | use example_common::*; | ||||||
|  | 
 | ||||||
|  | use cortex_m_rt::entry; | ||||||
|  | use embassy::flash::Flash; | ||||||
|  | use embassy_nrf::qspi; | ||||||
|  | use nrf52840_hal::gpio; | ||||||
|  | 
 | ||||||
|  | const PAGE_SIZE: usize = 4096; | ||||||
|  | 
 | ||||||
|  | // Workaround for alignment requirements.
 | ||||||
|  | // Nicer API will probably come in the future.
 | ||||||
|  | #[repr(C, align(4))] | ||||||
|  | struct AlignedBuf([u8; 4096]); | ||||||
|  | 
 | ||||||
|  | #[static_executor::task] | ||||||
|  | async fn run() { | ||||||
|  |     let p = embassy_nrf::pac::Peripherals::take().dewrap(); | ||||||
|  | 
 | ||||||
|  |     let port0 = gpio::p0::Parts::new(p.P0); | ||||||
|  | 
 | ||||||
|  |     let pins = qspi::Pins { | ||||||
|  |         csn: port0 | ||||||
|  |             .p0_17 | ||||||
|  |             .into_push_pull_output(gpio::Level::High) | ||||||
|  |             .degrade(), | ||||||
|  |         sck: port0 | ||||||
|  |             .p0_19 | ||||||
|  |             .into_push_pull_output(gpio::Level::High) | ||||||
|  |             .degrade(), | ||||||
|  |         io0: port0 | ||||||
|  |             .p0_20 | ||||||
|  |             .into_push_pull_output(gpio::Level::High) | ||||||
|  |             .degrade(), | ||||||
|  |         io1: port0 | ||||||
|  |             .p0_21 | ||||||
|  |             .into_push_pull_output(gpio::Level::High) | ||||||
|  |             .degrade(), | ||||||
|  |         io2: Some( | ||||||
|  |             port0 | ||||||
|  |                 .p0_22 | ||||||
|  |                 .into_push_pull_output(gpio::Level::High) | ||||||
|  |                 .degrade(), | ||||||
|  |         ), | ||||||
|  |         io3: Some( | ||||||
|  |             port0 | ||||||
|  |                 .p0_23 | ||||||
|  |                 .into_push_pull_output(gpio::Level::High) | ||||||
|  |                 .degrade(), | ||||||
|  |         ), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let config = qspi::Config { | ||||||
|  |         pins, | ||||||
|  |         read_opcode: qspi::ReadOpcode::READ4IO, | ||||||
|  |         write_opcode: qspi::WriteOpcode::PP4IO, | ||||||
|  |         xip_offset: 0, | ||||||
|  |         write_page_size: qspi::WritePageSize::_256BYTES, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let mut q = qspi::Qspi::new(p.QSPI, config); | ||||||
|  | 
 | ||||||
|  |     let mut id = [1; 3]; | ||||||
|  |     q.custom_instruction(0x9F, &[], &mut id).await.unwrap(); | ||||||
|  |     info!("id: {:[u8]}", id); | ||||||
|  | 
 | ||||||
|  |     // Read status register
 | ||||||
|  |     let mut status = [0; 1]; | ||||||
|  |     q.custom_instruction(0x05, &[], &mut status).await.unwrap(); | ||||||
|  | 
 | ||||||
|  |     info!("status: {:?}", status[0]); | ||||||
|  | 
 | ||||||
|  |     if status[0] & 0x40 == 0 { | ||||||
|  |         status[0] |= 0x40; | ||||||
|  | 
 | ||||||
|  |         q.custom_instruction(0x01, &status, &mut []).await.unwrap(); | ||||||
|  | 
 | ||||||
|  |         info!("enabled quad in status"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let mut buf = AlignedBuf([0u8; PAGE_SIZE]); | ||||||
|  | 
 | ||||||
|  |     let pattern = |a: u32| (a ^ (a >> 8) ^ (a >> 16) ^ (a >> 24)) as u8; | ||||||
|  | 
 | ||||||
|  |     for i in 0..8 { | ||||||
|  |         info!("page {:?}: erasing... ", i); | ||||||
|  |         q.erase(i * PAGE_SIZE).await.unwrap(); | ||||||
|  | 
 | ||||||
|  |         for j in 0..PAGE_SIZE { | ||||||
|  |             buf.0[j] = pattern((j + i * PAGE_SIZE) as u32); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         info!("programming..."); | ||||||
|  |         q.write(i * PAGE_SIZE, &buf.0).await.unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for i in 0..8 { | ||||||
|  |         info!("page {:?}: reading... ", i); | ||||||
|  |         q.read(i * PAGE_SIZE, &mut buf.0).await.unwrap(); | ||||||
|  | 
 | ||||||
|  |         info!("verifying..."); | ||||||
|  |         for j in 0..PAGE_SIZE { | ||||||
|  |             assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     info!("done!") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[entry] | ||||||
|  | fn main() -> ! { | ||||||
|  |     info!("Hello World!"); | ||||||
|  | 
 | ||||||
|  |     unsafe { | ||||||
|  |         run.spawn().dewrap(); | ||||||
|  |         static_executor::run(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								examples/src/bin/uart.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								examples/src/bin/uart.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![no_main] | ||||||
|  | #![feature(type_alias_impl_trait)] | ||||||
|  | 
 | ||||||
|  | #[path = "../example_common.rs"] | ||||||
|  | mod example_common; | ||||||
|  | use example_common::*; | ||||||
|  | 
 | ||||||
|  | use cortex_m_rt::entry; | ||||||
|  | use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; | ||||||
|  | use embassy_nrf::uarte; | ||||||
|  | use futures::pin_mut; | ||||||
|  | use nrf52840_hal::gpio; | ||||||
|  | 
 | ||||||
|  | #[static_executor::task] | ||||||
|  | async fn run() { | ||||||
|  |     let p = embassy_nrf::pac::Peripherals::take().dewrap(); | ||||||
|  | 
 | ||||||
|  |     let port0 = gpio::p0::Parts::new(p.P0); | ||||||
|  | 
 | ||||||
|  |     let pins = uarte::Pins { | ||||||
|  |         rxd: port0.p0_08.into_floating_input().degrade(), | ||||||
|  |         txd: port0 | ||||||
|  |             .p0_06 | ||||||
|  |             .into_push_pull_output(gpio::Level::Low) | ||||||
|  |             .degrade(), | ||||||
|  |         cts: None, | ||||||
|  |         rts: None, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     let u = uarte::Uarte::new( | ||||||
|  |         p.UARTE0, | ||||||
|  |         pins, | ||||||
|  |         uarte::Parity::EXCLUDED, | ||||||
|  |         uarte::Baudrate::BAUD115200, | ||||||
|  |     ); | ||||||
|  |     pin_mut!(u); | ||||||
|  | 
 | ||||||
|  |     info!("uarte initialized!"); | ||||||
|  | 
 | ||||||
|  |     u.write_all(b"Hello!\r\n").await.dewrap(); | ||||||
|  |     info!("wrote hello in uart!"); | ||||||
|  | 
 | ||||||
|  |     // Simple demo, reading 8-char chunks and echoing them back reversed.
 | ||||||
|  |     loop { | ||||||
|  |         info!("reading..."); | ||||||
|  |         let mut buf = [0u8; 8]; | ||||||
|  |         u.read_exact(&mut buf).await.dewrap(); | ||||||
|  |         info!("read done, got {:[u8]}", buf); | ||||||
|  | 
 | ||||||
|  |         // Reverse buf
 | ||||||
|  |         for i in 0..4 { | ||||||
|  |             let tmp = buf[i]; | ||||||
|  |             buf[i] = buf[7 - i]; | ||||||
|  |             buf[7 - i] = tmp; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         info!("writing..."); | ||||||
|  |         u.write_all(&buf).await.dewrap(); | ||||||
|  |         info!("write done"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[entry] | ||||||
|  | fn main() -> ! { | ||||||
|  |     info!("Hello World!"); | ||||||
|  | 
 | ||||||
|  |     unsafe { | ||||||
|  |         run.spawn().dewrap(); | ||||||
|  |         static_executor::run(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								examples/src/example_common.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								examples/src/example_common.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | #![macro_use] | ||||||
|  | 
 | ||||||
|  | use defmt_rtt as _; // global logger
 | ||||||
|  | use nrf52840_hal as _; | ||||||
|  | use panic_probe as _; | ||||||
|  | use static_executor_cortex_m as _; | ||||||
|  | 
 | ||||||
|  | pub use defmt::{info, intern}; | ||||||
|  | 
 | ||||||
|  | use core::sync::atomic::{AtomicUsize, Ordering}; | ||||||
|  | 
 | ||||||
|  | #[defmt::timestamp] | ||||||
|  | fn timestamp() -> u64 { | ||||||
|  |     static COUNT: AtomicUsize = AtomicUsize::new(0); | ||||||
|  |     // NOTE(no-CAS) `timestamps` runs with interrupts disabled
 | ||||||
|  |     let n = COUNT.load(Ordering::Relaxed); | ||||||
|  |     COUNT.store(n + 1, Ordering::Relaxed); | ||||||
|  |     n as u64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! depanic { | ||||||
|  |     ($( $i:expr ),*) => { | ||||||
|  |         { | ||||||
|  |             defmt::error!($( $i ),*); | ||||||
|  |             panic!(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait Dewrap<T> { | ||||||
|  |     /// dewrap = defmt unwrap
 | ||||||
|  |     fn dewrap(self) -> T; | ||||||
|  | 
 | ||||||
|  |     /// dexpect = defmt expect
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> Dewrap<T> for Option<T> { | ||||||
|  |     fn dewrap(self) -> T { | ||||||
|  |         match self { | ||||||
|  |             Some(t) => t, | ||||||
|  |             None => depanic!("Dewrap failed: enum is none"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T { | ||||||
|  |         match self { | ||||||
|  |             Some(t) => t, | ||||||
|  |             None => depanic!("Unexpected None: {:?}", msg), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> { | ||||||
|  |     fn dewrap(self) -> T { | ||||||
|  |         match self { | ||||||
|  |             Ok(t) => t, | ||||||
|  |             Err(e) => depanic!("Dewrap failed: {:?}", e), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn dexpect<M: defmt::Format>(self, msg: M) -> T { | ||||||
|  |         match self { | ||||||
|  |             Ok(t) => t, | ||||||
|  |             Err(e) => depanic!("Unexpected error: {:?}: {:?}", msg, e), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user