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…
Reference in New Issue
Block a user