├── .github └── workflows │ ├── embedded-builds.yml │ ├── fmt.yml │ └── host-builds.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── RELEASE.md ├── assets └── diagram-002.jpg ├── common └── phm-icd │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── lib.rs ├── examples └── feature-demos │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ └── bin │ ├── i2c-oled.rs │ ├── i2c.rs │ ├── spi.rs │ └── uart.rs ├── firmware ├── blackpill-phm │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── memory.x │ ├── src │ │ ├── lib.rs │ │ └── main.rs │ └── tests │ │ └── integration.rs ├── nrf52-phm │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── monotonic.rs │ │ └── uart.rs │ └── tests │ │ └── integration.rs ├── phm-worker │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── rp2040-phm │ ├── .cargo │ └── config.toml │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── memory.x │ ├── src │ ├── lib.rs │ └── main.rs │ └── tests │ └── integration.rs └── host ├── phm-cli ├── Cargo.lock ├── Cargo.toml ├── README.md └── src │ ├── cli.rs │ └── main.rs └── phm ├── Cargo.lock ├── Cargo.toml └── src └── lib.rs /.github/workflows/embedded-builds.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main] 4 | pull_request: 5 | branches: [main] 6 | 7 | name: Embedded Builds 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | include: 15 | - features: "" 16 | target: thumbv7em-none-eabihf 17 | rust: stable 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | profile: minimal 24 | toolchain: ${{ matrix.rust }} 25 | - run: rustup target add thumbv7em-none-eabihf 26 | - run: rustup target add thumbv6m-none-eabi 27 | 28 | - uses: actions-rs/cargo@v1 29 | with: 30 | command: build 31 | args: --manifest-path ./firmware/nrf52-phm/Cargo.toml --no-default-features --features=${{ matrix.feature }} --target=${{ matrix.target }} 32 | - uses: actions-rs/cargo@v1 33 | with: 34 | command: build 35 | args: --manifest-path ./firmware/phm-worker/Cargo.toml --no-default-features --features=${{ matrix.feature }} --target=${{ matrix.target }} 36 | - uses: actions-rs/cargo@v1 37 | with: 38 | command: build 39 | args: --manifest-path ./firmware/blackpill-phm/Cargo.toml --no-default-features --features=${{ matrix.feature }} --target=${{ matrix.target }} 40 | - uses: actions-rs/cargo@v1 41 | with: 42 | command: build 43 | args: --manifest-path ./firmware/rp2040-phm/Cargo.toml --no-default-features --features=${{ matrix.feature }} --target=thumbv6m-none-eabi 44 | -------------------------------------------------------------------------------- /.github/workflows/fmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main] 4 | pull_request: 5 | branches: [main] 6 | 7 | name: Formatting check 8 | 9 | jobs: 10 | fmt: 11 | name: Rustfmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: stable 19 | override: true 20 | components: rustfmt 21 | 22 | # Common directory 23 | - uses: actions-rs/cargo@v1 24 | with: 25 | command: fmt 26 | args: --manifest-path ./common/phm-icd/Cargo.toml -- --check 27 | 28 | # Host directory 29 | - uses: actions-rs/cargo@v1 30 | with: 31 | command: fmt 32 | args: --manifest-path ./host/phm-cli/Cargo.toml -- --check 33 | - uses: actions-rs/cargo@v1 34 | with: 35 | command: fmt 36 | args: --manifest-path ./host/phm/Cargo.toml -- --check 37 | 38 | # Examples directory 39 | - uses: actions-rs/cargo@v1 40 | with: 41 | command: fmt 42 | args: --manifest-path ./examples/feature-demos/Cargo.toml -- --check 43 | 44 | # Firmware directory 45 | - uses: actions-rs/cargo@v1 46 | with: 47 | command: fmt 48 | args: --manifest-path ./firmware/nrf52-phm/Cargo.toml -- --check 49 | - uses: actions-rs/cargo@v1 50 | with: 51 | command: fmt 52 | args: --manifest-path ./firmware/phm-worker/Cargo.toml -- --check 53 | - uses: actions-rs/cargo@v1 54 | with: 55 | command: fmt 56 | args: --manifest-path ./firmware/blackpill-phm/Cargo.toml -- --check 57 | - uses: actions-rs/cargo@v1 58 | with: 59 | command: fmt 60 | args: --manifest-path ./firmware/rp2040-phm/Cargo.toml -- --check 61 | -------------------------------------------------------------------------------- /.github/workflows/host-builds.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ main ] 4 | pull_request: 5 | branches: [ main ] 6 | 7 | name: Host Builds 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | include: 15 | - target: x86_64-unknown-linux-gnu 16 | rust: stable 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Install package 21 | run: | 22 | sudo apt-get -y install libudev-dev 23 | - uses: actions-rs/toolchain@v1 24 | with: 25 | profile: minimal 26 | toolchain: ${{ matrix.rust }} 27 | - uses: actions-rs/cargo@v1 28 | with: 29 | command: build 30 | args: --manifest-path ./host/phm/Cargo.toml --target=${{ matrix.target }} 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | command: build 34 | args: --manifest-path ./host/phm-cli/Cargo.toml --target=${{ matrix.target }} 35 | - uses: actions-rs/cargo@v1 36 | with: 37 | command: build 38 | args: --manifest-path ./examples/feature-demos/Cargo.toml --target=${{ matrix.target }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target/* 2 | 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Changes will be described here. 4 | 5 | ## Unreleased 6 | 7 | * Added `console` modes to CLI for I2C, SPI and UART and `listen` for UART [`#25`](https://github.com/jamesmunns/pretty-hal-machine/pull/25). 8 | * Added SPI commands to CLI [`#24`](https://github.com/jamesmunns/pretty-hal-machine/pull/24). 9 | * Collected basic feature demos into a common project [`#22`](https://github.com/jamesmunns/pretty-hal-machine/pull/22). 10 | 11 | ## 0.0.2 12 | 13 | * Added CLI with support for I2C commands [`#20`](https://github.com/jamesmunns/pretty-hal-machine/pull/20). 14 | * Added UART support [`#19`](https://github.com/jamesmunns/pretty-hal-machine/pull/19). 15 | * Added SPI support [`#18`](https://github.com/jamesmunns/pretty-hal-machine/pull/18). 16 | * Added I2C OLED display example [`#14`](https://github.com/jamesmunns/pretty-hal-machine/pull/14). 17 | * Added more I2C `embedded-hal` trait implementations [`#8`](https://github.com/jamesmunns/pretty-hal-machine/pull/8). 18 | * Added MCU implementation for the `blackpill (stm32f411)` board [`#6`](https://github.com/jamesmunns/pretty-hal-machine/pull/6). 19 | * Added a worker crate that can be shared between different MCU implementations [`#5`](https://github.com/jamesmunns/pretty-hal-machine/pull/5). 20 | * Added MCU implementation for the `rp pico (rp2040)` board [`#4`](https://github.com/jamesmunns/pretty-hal-machine/pull/4). 21 | 22 | ## 0.0.1 23 | 24 | * Initial release. 25 | * I2C support. 26 | * MCU implementation for nrf52840. 27 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pretty HAL machine 2 | 3 | Here's a quick diagram showing how `pretty HAL machine` can be used for developing Embedded Rust drivers: 4 | 5 | ![Stream Diagram](./assets/diagram-002.jpg) 6 | 7 | More info soon! For now you can watch the stream where I wrote the 8 | USB-to-I2C adapter for the splitpea software [on youtube!](https://www.youtube.com/watch?v=2S6G7wd8Kpo). 9 | 10 | The goal of this project is to make a "PC to any protocol adapter" using cheap, off the shelf microcontrollers. 11 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release checkout procedure 2 | 3 | * checkout a new branch (e.g. `release-vx.y.z`) 4 | * Update version numbers (including at least), in this order, updating version of deps too (all crates should have same version number for now, even if no changes): 5 | * common/phm-icd 6 | * host/phm 7 | * host/phm-cli 8 | * firmware/phm-worker 9 | * (unpublished crates just use path deps) 10 | * Commit 11 | * `cargo publish` each of (at least) the following crates, in this order: 12 | * common/phm-icd 13 | * host/phm 14 | * host/phm-cli 15 | * firmware/phm-worker 16 | * `git tag` each of (at least) the following tags, using the form `$CRATE-$VERSION`, e.g. `phm-v0.0.1` 17 | * phm-icd 18 | * phm 19 | * phm-cli 20 | * phm-worker 21 | * `git push --tags origin $BRANCH`, and open a pull request for the new release. It should be merged without changes. 22 | -------------------------------------------------------------------------------- /assets/diagram-002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesmunns/pretty-hal-machine/f9eb128409682e1fbeeec745c9e119da9dbbf6af/assets/diagram-002.jpg -------------------------------------------------------------------------------- /common/phm-icd/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | 3 | -------------------------------------------------------------------------------- /common/phm-icd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phm-icd" 3 | version = "0.0.2" 4 | description = "The Interface Control Document (ICD) for Pretty HAL Machine" 5 | repository = "https://github.com/jamesmunns/pretty-hal-machine" 6 | authors = [ 7 | "James Munns ", 8 | "Henrik Alsér ", 9 | ] 10 | edition = "2021" 11 | readme = "../../README.md" 12 | 13 | categories = [ 14 | "embedded", 15 | ] 16 | license = "MIT OR Apache-2.0" 17 | 18 | [dependencies.heapless] 19 | version = "0.7.10" 20 | features = ["serde"] 21 | 22 | [dependencies.defmt] 23 | version = "0.3.0" 24 | optional = true 25 | 26 | [dependencies.serde] 27 | version = "1.0.136" 28 | default-features = false 29 | features = ["derive"] 30 | 31 | [features] 32 | use-defmt = ["defmt", "heapless/defmt-impl"] 33 | 34 | [package.metadata.docs.rs] 35 | all-features = true 36 | -------------------------------------------------------------------------------- /common/phm-icd/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use heapless::Vec; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | // TODO: Something better than this 7 | pub type Error = (); 8 | 9 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 10 | #[derive(Debug, Serialize, Deserialize)] 11 | pub enum ToMcu { 12 | I2c(ToMcuI2c), 13 | Spi(ToMcuSpi), 14 | Uart(ToMcuUart), 15 | Ping, 16 | } 17 | 18 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 19 | #[derive(Debug, Serialize, Deserialize)] 20 | pub enum ToMcuI2c { 21 | Write { 22 | addr: u8, 23 | output: Vec, 24 | }, 25 | Read { 26 | addr: u8, 27 | to_read: u32, 28 | }, 29 | WriteThenRead { 30 | addr: u8, 31 | output: Vec, 32 | to_read: u32, 33 | }, 34 | } 35 | 36 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 37 | #[derive(Debug, Serialize, Deserialize)] 38 | pub enum ToMcuSpi { 39 | Write { output: Vec }, 40 | Transfer { output: Vec }, 41 | } 42 | 43 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 44 | #[derive(Debug, Serialize, Deserialize)] 45 | pub enum ToMcuUart { 46 | Write { output: Vec }, 47 | Flush, 48 | Read, 49 | } 50 | 51 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 52 | #[derive(Debug, Serialize, Deserialize)] 53 | pub enum ToPc { 54 | I2c(ToPcI2c), 55 | Spi(ToPcSpi), 56 | Uart(ToPcUart), 57 | Pong, 58 | } 59 | 60 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 61 | #[derive(Debug, Serialize, Deserialize)] 62 | pub enum ToPcI2c { 63 | WriteComplete { addr: u8 }, 64 | Read { addr: u8, data_read: Vec }, 65 | WriteThenRead { addr: u8, data_read: Vec }, 66 | } 67 | 68 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 69 | #[derive(Debug, Serialize, Deserialize)] 70 | pub enum ToPcSpi { 71 | WriteComplete, 72 | Transfer { data_read: Vec }, 73 | } 74 | 75 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 76 | #[derive(Debug, Serialize, Deserialize)] 77 | pub enum ToPcUart { 78 | WriteComplete, 79 | Read { data_read: Vec }, 80 | } 81 | -------------------------------------------------------------------------------- /examples/feature-demos/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "CoreFoundation-sys" 7 | version = "0.1.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b" 10 | dependencies = [ 11 | "libc", 12 | "mach 0.1.2", 13 | ] 14 | 15 | [[package]] 16 | name = "IOKit-sys" 17 | version = "0.1.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a" 20 | dependencies = [ 21 | "CoreFoundation-sys", 22 | "libc", 23 | "mach 0.1.2", 24 | ] 25 | 26 | [[package]] 27 | name = "aho-corasick" 28 | version = "0.7.18" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 31 | dependencies = [ 32 | "memchr", 33 | ] 34 | 35 | [[package]] 36 | name = "atomic-polyfill" 37 | version = "0.1.6" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "ee6adc1648f03fbc1bc1b5cf0f2fdfb5edbc96215b711edcfe6ce2641ef9b347" 40 | dependencies = [ 41 | "critical-section", 42 | "riscv-target", 43 | ] 44 | 45 | [[package]] 46 | name = "az" 47 | version = "1.2.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4" 50 | 51 | [[package]] 52 | name = "bare-metal" 53 | version = "0.2.5" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 56 | dependencies = [ 57 | "rustc_version", 58 | ] 59 | 60 | [[package]] 61 | name = "bare-metal" 62 | version = "1.0.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 65 | 66 | [[package]] 67 | name = "bit_field" 68 | version = "0.10.1" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 71 | 72 | [[package]] 73 | name = "bitfield" 74 | version = "0.13.2" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 77 | 78 | [[package]] 79 | name = "bitflags" 80 | version = "1.3.2" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 83 | 84 | [[package]] 85 | name = "byte-slice-cast" 86 | version = "0.3.5" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" 89 | 90 | [[package]] 91 | name = "byteorder" 92 | version = "1.4.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 95 | 96 | [[package]] 97 | name = "cc" 98 | version = "1.0.72" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 101 | 102 | [[package]] 103 | name = "cfg-if" 104 | version = "0.1.10" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 107 | 108 | [[package]] 109 | name = "cfg-if" 110 | version = "1.0.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 113 | 114 | [[package]] 115 | name = "cortex-m" 116 | version = "0.7.4" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" 119 | dependencies = [ 120 | "bare-metal 0.2.5", 121 | "bitfield", 122 | "embedded-hal", 123 | "volatile-register", 124 | ] 125 | 126 | [[package]] 127 | name = "critical-section" 128 | version = "0.2.5" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" 131 | dependencies = [ 132 | "bare-metal 1.0.0", 133 | "cfg-if 1.0.0", 134 | "cortex-m", 135 | "riscv", 136 | ] 137 | 138 | [[package]] 139 | name = "display-interface" 140 | version = "0.4.1" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "7517c040926d7b02b111884aa089177db80878533127f7c1b480d852c5fb4112" 143 | 144 | [[package]] 145 | name = "display-interface-i2c" 146 | version = "0.4.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "4895cd4e54e5536ef370d7f1eec787aad8275dd8ad15815aebfa71dd847b4ebf" 149 | dependencies = [ 150 | "display-interface", 151 | "embedded-hal", 152 | ] 153 | 154 | [[package]] 155 | name = "display-interface-spi" 156 | version = "0.4.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "489378ad054862146fbd1f09f51d585ccbe4bd1e2feadcda2a13ac33f840e1a5" 159 | dependencies = [ 160 | "byte-slice-cast", 161 | "display-interface", 162 | "embedded-hal", 163 | ] 164 | 165 | [[package]] 166 | name = "embedded-graphics-core" 167 | version = "0.3.3" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "b8b1239db5f3eeb7e33e35bd10bd014e7b2537b17e071f726a09351431337cfa" 170 | dependencies = [ 171 | "az", 172 | "byteorder", 173 | ] 174 | 175 | [[package]] 176 | name = "embedded-hal" 177 | version = "0.2.7" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 180 | dependencies = [ 181 | "nb 0.1.3", 182 | "void", 183 | ] 184 | 185 | [[package]] 186 | name = "hash32" 187 | version = "0.2.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 190 | dependencies = [ 191 | "byteorder", 192 | ] 193 | 194 | [[package]] 195 | name = "heapless" 196 | version = "0.7.10" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" 199 | dependencies = [ 200 | "atomic-polyfill", 201 | "hash32", 202 | "serde", 203 | "spin", 204 | "stable_deref_trait", 205 | ] 206 | 207 | [[package]] 208 | name = "i2c-oled-example" 209 | version = "0.0.2" 210 | dependencies = [ 211 | "embedded-hal", 212 | "phm", 213 | "serialport", 214 | "ssd1306", 215 | ] 216 | 217 | [[package]] 218 | name = "lazy_static" 219 | version = "1.4.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 222 | 223 | [[package]] 224 | name = "libc" 225 | version = "0.2.117" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" 228 | 229 | [[package]] 230 | name = "libudev" 231 | version = "0.2.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe" 234 | dependencies = [ 235 | "libc", 236 | "libudev-sys", 237 | ] 238 | 239 | [[package]] 240 | name = "libudev-sys" 241 | version = "0.1.4" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 244 | dependencies = [ 245 | "libc", 246 | "pkg-config", 247 | ] 248 | 249 | [[package]] 250 | name = "lock_api" 251 | version = "0.4.6" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 254 | dependencies = [ 255 | "scopeguard", 256 | ] 257 | 258 | [[package]] 259 | name = "mach" 260 | version = "0.1.2" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" 263 | dependencies = [ 264 | "libc", 265 | ] 266 | 267 | [[package]] 268 | name = "mach" 269 | version = "0.2.3" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" 272 | dependencies = [ 273 | "libc", 274 | ] 275 | 276 | [[package]] 277 | name = "memchr" 278 | version = "2.4.1" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 281 | 282 | [[package]] 283 | name = "nb" 284 | version = "0.1.3" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 287 | dependencies = [ 288 | "nb 1.0.0", 289 | ] 290 | 291 | [[package]] 292 | name = "nb" 293 | version = "1.0.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 296 | 297 | [[package]] 298 | name = "nix" 299 | version = "0.16.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb" 302 | dependencies = [ 303 | "bitflags", 304 | "cc", 305 | "cfg-if 0.1.10", 306 | "libc", 307 | "void", 308 | ] 309 | 310 | [[package]] 311 | name = "phm" 312 | version = "0.0.2" 313 | dependencies = [ 314 | "embedded-hal", 315 | "heapless", 316 | "nb 1.0.0", 317 | "phm-icd", 318 | "postcard", 319 | "serde", 320 | "serialport", 321 | ] 322 | 323 | [[package]] 324 | name = "phm-icd" 325 | version = "0.0.2" 326 | dependencies = [ 327 | "heapless", 328 | "serde", 329 | ] 330 | 331 | [[package]] 332 | name = "pkg-config" 333 | version = "0.3.24" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 336 | 337 | [[package]] 338 | name = "postcard" 339 | version = "0.7.3" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" 342 | dependencies = [ 343 | "heapless", 344 | "postcard-cobs", 345 | "serde", 346 | ] 347 | 348 | [[package]] 349 | name = "postcard-cobs" 350 | version = "0.1.5-pre" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" 353 | 354 | [[package]] 355 | name = "proc-macro2" 356 | version = "1.0.36" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 359 | dependencies = [ 360 | "unicode-xid", 361 | ] 362 | 363 | [[package]] 364 | name = "quote" 365 | version = "1.0.15" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 368 | dependencies = [ 369 | "proc-macro2", 370 | ] 371 | 372 | [[package]] 373 | name = "regex" 374 | version = "1.5.4" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 377 | dependencies = [ 378 | "aho-corasick", 379 | "memchr", 380 | "regex-syntax", 381 | ] 382 | 383 | [[package]] 384 | name = "regex-syntax" 385 | version = "0.6.25" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 388 | 389 | [[package]] 390 | name = "riscv" 391 | version = "0.7.0" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 394 | dependencies = [ 395 | "bare-metal 1.0.0", 396 | "bit_field", 397 | "riscv-target", 398 | ] 399 | 400 | [[package]] 401 | name = "riscv-target" 402 | version = "0.1.2" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 405 | dependencies = [ 406 | "lazy_static", 407 | "regex", 408 | ] 409 | 410 | [[package]] 411 | name = "rustc_version" 412 | version = "0.2.3" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 415 | dependencies = [ 416 | "semver", 417 | ] 418 | 419 | [[package]] 420 | name = "scopeguard" 421 | version = "1.1.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 424 | 425 | [[package]] 426 | name = "semver" 427 | version = "0.9.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 430 | dependencies = [ 431 | "semver-parser", 432 | ] 433 | 434 | [[package]] 435 | name = "semver-parser" 436 | version = "0.7.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 439 | 440 | [[package]] 441 | name = "serde" 442 | version = "1.0.136" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 445 | dependencies = [ 446 | "serde_derive", 447 | ] 448 | 449 | [[package]] 450 | name = "serde_derive" 451 | version = "1.0.136" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 454 | dependencies = [ 455 | "proc-macro2", 456 | "quote", 457 | "syn", 458 | ] 459 | 460 | [[package]] 461 | name = "serialport" 462 | version = "4.0.1" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "5d8cd7c0f22290ee2c01457009fa6fc1cae4153d5608a924e5dc423babc2c655" 465 | dependencies = [ 466 | "CoreFoundation-sys", 467 | "IOKit-sys", 468 | "bitflags", 469 | "cfg-if 0.1.10", 470 | "libudev", 471 | "mach 0.2.3", 472 | "nix", 473 | "regex", 474 | "winapi", 475 | ] 476 | 477 | [[package]] 478 | name = "spin" 479 | version = "0.9.2" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" 482 | dependencies = [ 483 | "lock_api", 484 | ] 485 | 486 | [[package]] 487 | name = "ssd1306" 488 | version = "0.7.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "a1fe1e39676a72e07a9592c8a30a008e58746b47e872c81ca909b1c2f992001b" 491 | dependencies = [ 492 | "display-interface", 493 | "display-interface-i2c", 494 | "display-interface-spi", 495 | "embedded-graphics-core", 496 | "embedded-hal", 497 | ] 498 | 499 | [[package]] 500 | name = "stable_deref_trait" 501 | version = "1.2.0" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 504 | 505 | [[package]] 506 | name = "syn" 507 | version = "1.0.86" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 510 | dependencies = [ 511 | "proc-macro2", 512 | "quote", 513 | "unicode-xid", 514 | ] 515 | 516 | [[package]] 517 | name = "unicode-xid" 518 | version = "0.2.2" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 521 | 522 | [[package]] 523 | name = "vcell" 524 | version = "0.1.3" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 527 | 528 | [[package]] 529 | name = "void" 530 | version = "1.0.2" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 533 | 534 | [[package]] 535 | name = "volatile-register" 536 | version = "0.2.1" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" 539 | dependencies = [ 540 | "vcell", 541 | ] 542 | 543 | [[package]] 544 | name = "winapi" 545 | version = "0.3.9" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 548 | dependencies = [ 549 | "winapi-i686-pc-windows-gnu", 550 | "winapi-x86_64-pc-windows-gnu", 551 | ] 552 | 553 | [[package]] 554 | name = "winapi-i686-pc-windows-gnu" 555 | version = "0.4.0" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 558 | 559 | [[package]] 560 | name = "winapi-x86_64-pc-windows-gnu" 561 | version = "0.4.0" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 564 | -------------------------------------------------------------------------------- /examples/feature-demos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i2c-oled-example" 3 | version = "0.0.2" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | embedded-hal = "0.2.6" 10 | serialport = "4.0.1" 11 | ssd1306 = "0.7.0" 12 | 13 | [dependencies.phm] 14 | path = "../../host/phm" 15 | 16 | -------------------------------------------------------------------------------- /examples/feature-demos/README.md: -------------------------------------------------------------------------------- 1 | # Feature demo collection 2 | 3 | ## I2C with OLED display driver 4 | 5 | ``` console 6 | $ cargo run --bin i2c-oled 7 | ``` 8 | 9 | [![YouTube video showing the OLED displaying text](https://img.youtube.com/vi/0sJZpEWOLNc/0.jpg)](http://www.youtube.com/watch?v=0sJZpEWOLNc "pretty HAL machine demo 1") 10 | 11 | 12 | 13 | ## UART 14 | 15 | ``` console 16 | $ cargo run --bin uart 17 | ``` 18 | Continuously reading incoming bytes + transmitting data once a second. 19 | 20 | 21 | 22 | ## SPI 23 | 24 | ``` console 25 | $ cargo run --bin spi 26 | ``` 27 | Performs an SPI transfer once a second. 28 | 29 | 30 | 31 | ## I2C 32 | 33 | ``` console 34 | $ cargo run --bin i2c 35 | ``` 36 | Writes data to I2C address `0x42` once a second. -------------------------------------------------------------------------------- /examples/feature-demos/src/bin/i2c-oled.rs: -------------------------------------------------------------------------------- 1 | // $ cargo run --bin i2c-oled 2 | use core::fmt::Write; 3 | use phm::Machine; 4 | use std::time::{Duration, Instant}; 5 | 6 | use ssd1306::{ 7 | prelude::*, rotation::DisplayRotation, size::DisplaySize128x64, I2CDisplayInterface, Ssd1306, 8 | }; 9 | 10 | fn main() -> Result<(), ()> { 11 | println!("I2C OLED display driver demo!"); 12 | 13 | let mut dport = None; 14 | 15 | for port in serialport::available_ports().unwrap() { 16 | if let serialport::SerialPortType::UsbPort(serialport::UsbPortInfo { 17 | serial_number: Some(sn), 18 | .. 19 | }) = &port.port_type 20 | { 21 | if sn.as_str() == "ajm123" { 22 | dport = Some(port.clone()); 23 | break; 24 | } 25 | } 26 | } 27 | 28 | let dport = if let Some(port) = dport { 29 | port 30 | } else { 31 | eprintln!("Error: No `Pretty hal machine` connected!"); 32 | return Ok(()); 33 | }; 34 | 35 | let port = serialport::new(dport.port_name, 115200) 36 | .timeout(Duration::from_millis(5)) 37 | .open() 38 | .map_err(drop)?; 39 | 40 | let ehal = Machine::from_port(port).unwrap(); 41 | 42 | // Configure the OLED display. 43 | let interface = I2CDisplayInterface::new(ehal); 44 | let mut disp = 45 | Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0).into_terminal_mode(); 46 | disp.init().ok(); 47 | disp.clear().ok(); 48 | disp.write_str("Hello world!\n").ok(); 49 | 50 | let mut last_send = Instant::now(); 51 | 52 | loop { 53 | if last_send.elapsed() >= Duration::from_secs(1) { 54 | println!("Sending command!"); 55 | disp.write_str("PHM!\n").ok(); 56 | last_send = Instant::now(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/feature-demos/src/bin/i2c.rs: -------------------------------------------------------------------------------- 1 | // $ cargo run --bin i2c 2 | use phm::Machine; 3 | use std::time::{Duration, Instant}; 4 | 5 | fn main() -> Result<(), ()> { 6 | println!("I2C write demo!"); 7 | 8 | let mut dport = None; 9 | 10 | for port in serialport::available_ports().unwrap() { 11 | if let serialport::SerialPortType::UsbPort(serialport::UsbPortInfo { 12 | serial_number: Some(sn), 13 | .. 14 | }) = &port.port_type 15 | { 16 | if sn.as_str() == "ajm123" { 17 | dport = Some(port.clone()); 18 | break; 19 | } 20 | } 21 | } 22 | 23 | let dport = if let Some(port) = dport { 24 | port 25 | } else { 26 | eprintln!("Error: No `Pretty hal machine` connected!"); 27 | return Ok(()); 28 | }; 29 | 30 | let port = serialport::new(dport.port_name, 115200) 31 | .timeout(Duration::from_millis(5)) 32 | .open() 33 | .map_err(drop)?; 34 | 35 | let mut ehal = Machine::from_port(port).unwrap(); 36 | 37 | let mut last_send = Instant::now(); 38 | 39 | loop { 40 | if last_send.elapsed() >= Duration::from_secs(1) { 41 | println!("Sending I2C command!"); 42 | embedded_hal::blocking::i2c::Write::write(&mut ehal, 0x42, &[1, 2, 3, 4]).unwrap(); 43 | 44 | last_send = Instant::now(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/feature-demos/src/bin/spi.rs: -------------------------------------------------------------------------------- 1 | // $ cargo run --bin spi 2 | use phm::Machine; 3 | use std::time::{Duration, Instant}; 4 | 5 | fn main() -> Result<(), ()> { 6 | println!("SPI transfer demo!"); 7 | 8 | let mut dport = None; 9 | 10 | for port in serialport::available_ports().unwrap() { 11 | if let serialport::SerialPortType::UsbPort(serialport::UsbPortInfo { 12 | serial_number: Some(sn), 13 | .. 14 | }) = &port.port_type 15 | { 16 | if sn.as_str() == "ajm123" { 17 | dport = Some(port.clone()); 18 | break; 19 | } 20 | } 21 | } 22 | 23 | let dport = if let Some(port) = dport { 24 | port 25 | } else { 26 | eprintln!("Error: No `Pretty hal machine` connected!"); 27 | return Ok(()); 28 | }; 29 | 30 | let port = serialport::new(dport.port_name, 115200) 31 | .timeout(Duration::from_millis(5)) 32 | .open() 33 | .map_err(drop)?; 34 | 35 | let mut ehal = Machine::from_port(port).unwrap(); 36 | 37 | let mut last_send = Instant::now(); 38 | 39 | loop { 40 | if last_send.elapsed() >= Duration::from_secs(1) { 41 | // println!("Sending I2C command!"); 42 | // embedded_hal::blocking::i2c::Write::write(&mut ehal, 0x42, &[1, 2, 3, 4]).unwrap(); 43 | 44 | let mut buf = [1, 2, 3, 4]; 45 | println!("Sending SPI: {:?}", buf); 46 | embedded_hal::blocking::spi::Transfer::transfer(&mut ehal, &mut buf).unwrap(); 47 | println!("Received SPI: {:?}", buf); 48 | last_send = Instant::now(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/feature-demos/src/bin/uart.rs: -------------------------------------------------------------------------------- 1 | // $ cargo run --bin uart 2 | use phm::Machine; 3 | use std::io::Write; 4 | use std::time::{Duration, Instant}; 5 | 6 | fn main() -> Result<(), ()> { 7 | println!("UART demo!"); 8 | 9 | let mut dport = None; 10 | 11 | for port in serialport::available_ports().unwrap() { 12 | if let serialport::SerialPortType::UsbPort(serialport::UsbPortInfo { 13 | serial_number: Some(sn), 14 | .. 15 | }) = &port.port_type 16 | { 17 | if sn.as_str() == "ajm123" { 18 | dport = Some(port.clone()); 19 | break; 20 | } 21 | } 22 | } 23 | 24 | let dport = if let Some(port) = dport { 25 | port 26 | } else { 27 | eprintln!("Error: No `Pretty hal machine` connected!"); 28 | return Ok(()); 29 | }; 30 | 31 | let port = serialport::new(dport.port_name, 115200) 32 | .timeout(Duration::from_millis(5)) 33 | .open() 34 | .map_err(drop)?; 35 | 36 | let mut ehal = Machine::from_port(port).unwrap(); 37 | 38 | embedded_hal::blocking::serial::Write::::bflush(&mut ehal).unwrap(); 39 | 40 | let mut last_send = Instant::now(); 41 | 42 | loop { 43 | if last_send.elapsed() >= Duration::from_secs(1) { 44 | let str = "Pretty HAL machine!\n"; 45 | print!("TX: {}", str); 46 | embedded_hal::blocking::serial::Write::::bwrite_all(&mut ehal, str.as_bytes()) 47 | .unwrap(); 48 | 49 | last_send = Instant::now(); 50 | } 51 | while let Ok(b) = embedded_hal::serial::Read::::read(&mut ehal) { 52 | print!("{:02x} ", b); 53 | } 54 | std::io::stdout().flush().unwrap(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 2 | # TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) 3 | runner = "probe-run --chip STM32F411RETx" 4 | rustflags = [ 5 | "-C", "linker=flip-link", 6 | "-C", "link-arg=-Tlink.x", 7 | "-C", "link-arg=-Tdefmt.x", 8 | # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x 9 | # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 10 | "-C", "link-arg=--nmagic", 11 | ] 12 | 13 | [build] 14 | # TODO(3) Adjust the compilation target. 15 | # (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right 16 | # target improves performance) 17 | # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 18 | # target = "thumbv7m-none-eabi" # Cortex-M3 19 | # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 20 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 21 | 22 | [alias] 23 | rb = "run --bin" 24 | rrb = "run --release --bin" -------------------------------------------------------------------------------- /firmware/blackpill-phm/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // override the default setting (`cargo check --all-targets`) which produces the following error 3 | // "can't find crate for `test`" when the default compilation target is a no_std target 4 | // with these changes RA will call `cargo check --bins` on save 5 | "rust-analyzer.checkOnSave.allTargets": false, 6 | "rust-analyzer.checkOnSave.extraArgs": [ 7 | "--bins" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["James Munns ", "Henrik Alsér "] 3 | name = "blackpill-phm" 4 | edition = "2021" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | harness = false 9 | 10 | # needed for each integration test 11 | [[test]] 12 | name = "integration" 13 | harness = false 14 | 15 | [dependencies] 16 | cortex-m = "0.7.3" 17 | cortex-m-rt = "0.7.0" 18 | cortex-m-rtic = "1.0.0" 19 | rtic-monotonic = "1.0.0" 20 | fugit = "0.3.3" 21 | defmt = "0.3.0" 22 | defmt-rtt = "0.3.0" 23 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } 24 | stm32f4xx-hal = { version = "0.11.1", features = ["rtic", "stm32f411", "usb_fs"] } 25 | usb-device = "0.2.8" 26 | usbd-serial = "0.1.1" 27 | postcard = "0.7.2" 28 | embedded-hal = "0.2.6" 29 | 30 | [dependencies.heapless] 31 | version = "0.7.10" 32 | features = ["serde"] 33 | 34 | [dependencies.phm-icd] 35 | path = "../../common/phm-icd" 36 | features = ["use-defmt"] 37 | 38 | [dependencies.phm-worker] 39 | path = "../phm-worker" 40 | 41 | [dev-dependencies] 42 | defmt-test = "0.3.0" 43 | 44 | # cargo build/run 45 | [profile.dev] 46 | codegen-units = 1 47 | debug = 2 48 | debug-assertions = true # <- 49 | incremental = false 50 | opt-level = 3 # <- 51 | overflow-checks = true # <- 52 | 53 | # cargo test 54 | [profile.test] 55 | codegen-units = 1 56 | debug = 2 57 | debug-assertions = true # <- 58 | incremental = false 59 | opt-level = 3 # <- 60 | overflow-checks = true # <- 61 | 62 | # cargo build/run --release 63 | [profile.release] 64 | codegen-units = 1 65 | debug = 2 66 | debug-assertions = false # <- 67 | incremental = false 68 | # NOTE disabled to work around issue rust-lang/rust#90357 69 | # the bug results in log messages not having location information 70 | # (the line printed below the log message that contains the file-line location) 71 | # lto = 'fat' 72 | opt-level = 3 # <- 73 | overflow-checks = false # <- 74 | 75 | # cargo test --release 76 | [profile.bench] 77 | codegen-units = 1 78 | debug = 2 79 | debug-assertions = false # <- 80 | incremental = false 81 | # see comment in the profile.release section 82 | lto = 'false' 83 | opt-level = 3 # <- 84 | overflow-checks = false # <- 85 | 86 | # uncomment this to switch from the crates.io version of defmt to its git version 87 | # check app-template's README for instructions 88 | # [patch.crates-io] 89 | # defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 90 | # defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 91 | # defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 92 | # panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 93 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/README.md: -------------------------------------------------------------------------------- 1 | # `app-template` 2 | 3 | > Quickly set up a [`probe-run`] + [`defmt`] + [`flip-link`] embedded project 4 | 5 | [`probe-run`]: https://crates.io/crates/probe-run 6 | [`defmt`]: https://github.com/knurling-rs/defmt 7 | [`flip-link`]: https://github.com/knurling-rs/flip-link 8 | 9 | ## Dependencies 10 | 11 | #### 1. `flip-link`: 12 | 13 | ```console 14 | $ cargo install flip-link 15 | ``` 16 | 17 | #### 2. `probe-run`: 18 | 19 | ``` console 20 | $ # make sure to install v0.2.0 or later 21 | $ cargo install probe-run 22 | ``` 23 | 24 | #### 3. [`cargo-generate`]: 25 | 26 | ``` console 27 | $ cargo install cargo-generate 28 | ``` 29 | 30 | [`cargo-generate`]: https://crates.io/crates/cargo-generate 31 | 32 | > *Note:* You can also just clone this repository instead of using `cargo-generate`, but this involves additional manual adjustments. 33 | 34 | ## Setup 35 | 36 | #### 1. Initialize the project template 37 | 38 | ``` console 39 | $ cargo generate \ 40 | --git https://github.com/knurling-rs/app-template \ 41 | --branch main \ 42 | --name my-app 43 | ``` 44 | 45 | If you look into your new `my-app` folder, you'll find that there are a few `TODO`s in the files marking the properties you need to set. 46 | 47 | Let's walk through them together now. 48 | 49 | #### 2. Set `probe-run` chip 50 | 51 | Pick a chip from `probe-run --list-chips` and enter it into `.cargo/config.toml`. 52 | 53 | If, for example, you have a nRF52840 Development Kit from one of [our workshops], replace `{{chip}}` with `nRF52840_xxAA`. 54 | 55 | [our workshops]: https://github.com/ferrous-systems/embedded-trainings-2020 56 | 57 | ``` diff 58 | # .cargo/config.toml 59 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 60 | -runner = "probe-run --chip {{chip}}" 61 | +runner = "probe-run --chip nRF52840_xxAA" 62 | ``` 63 | 64 | #### 3. Adjust the compilation target 65 | 66 | In `.cargo/config.toml`, pick the right compilation target for your board. 67 | 68 | ``` diff 69 | # .cargo/config.toml 70 | [build] 71 | -target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 72 | -# target = "thumbv7m-none-eabi" # Cortex-M3 73 | -# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 74 | -# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 75 | +target = "thumbv7em-none-eabihf" # Cortex-M4F (with FPU) 76 | ``` 77 | 78 | Add the target with `rustup`. 79 | 80 | ``` console 81 | $ rustup target add thumbv7em-none-eabihf 82 | ``` 83 | 84 | #### 4. Add a HAL as a dependency 85 | 86 | In `Cargo.toml`, list the Hardware Abstraction Layer (HAL) for your board as a dependency. 87 | 88 | For the nRF52840 you'll want to use the [`nrf52840-hal`]. 89 | 90 | [`nrf52840-hal`]: https://crates.io/crates/nrf52840-hal 91 | 92 | ``` diff 93 | # Cargo.toml 94 | [dependencies] 95 | -# some-hal = "1.2.3" 96 | +nrf52840-hal = "0.14.0" 97 | ``` 98 | 99 | #### 5. Import your HAL 100 | 101 | Now that you have selected a HAL, fix the HAL import in `src/lib.rs` 102 | 103 | ``` diff 104 | // my-app/src/lib.rs 105 | -// use some_hal as _; // memory layout 106 | +use nrf52840_hal as _; // memory layout 107 | ``` 108 | 109 | #### (6. Get a linker script) 110 | 111 | Some HAL crates require that you manually copy over a file called `memory.x` from the HAL to the root of your project. For nrf52840-hal, this is done automatically so no action is needed. For other HAL crates, you can get it from your local Cargo folder, the default location is under: 112 | 113 | ``` 114 | ~/.cargo/registry/src/ 115 | ``` 116 | 117 | Not all HALs provide a `memory.x` file, you may need to write it yourself. Check the documentation for the HAL you are using. 118 | 119 | 120 | #### 7. Run! 121 | 122 | You are now all set to `cargo-run` your first `defmt`-powered application! 123 | There are some examples in the `src/bin` directory. 124 | 125 | Start by `cargo run`-ning `my-app/src/bin/hello.rs`: 126 | 127 | ``` console 128 | $ # `rb` is an alias for `run --bin` 129 | $ cargo rb hello 130 | Finished dev [optimized + debuginfo] target(s) in 0.03s 131 | flashing program .. 132 | DONE 133 | resetting device 134 | 0.000000 INFO Hello, world! 135 | (..) 136 | 137 | $ echo $? 138 | 0 139 | ``` 140 | 141 | If you're running out of memory (`flip-link` bails with an overflow error), you can decrease the size of the device memory buffer by setting the `DEFMT_RTT_BUFFER_SIZE` environment variable. The default value is 1024 bytes, and powers of two should be used for optimal performance: 142 | 143 | ``` console 144 | $ DEFMT_RTT_BUFFER_SIZE=64 cargo rb hello 145 | ``` 146 | 147 | #### (8. Set `rust-analyzer.linkedProjects`) 148 | 149 | If you are using [rust-analyzer] with VS Code for IDE-like features you can add following configuration to your `.vscode/settings.json` to make it work transparently across workspaces. Find the details of this option in the [RA docs]. 150 | 151 | ```json 152 | { 153 | "rust-analyzer.linkedProjects": [ 154 | "Cargo.toml", 155 | "firmware/Cargo.toml", 156 | ] 157 | } 158 | ``` 159 | 160 | [RA docs]: https://rust-analyzer.github.io/manual.html#configuration 161 | [rust-analyzer]: https://rust-analyzer.github.io/ 162 | 163 | ## Running tests 164 | 165 | The template comes configured for running unit tests and integration tests on the target. 166 | 167 | Unit tests reside in the library crate and can test private API; the initial set of unit tests are in `src/lib.rs`. 168 | `cargo test --lib` will run those unit tests. 169 | 170 | ``` console 171 | $ cargo test --lib 172 | (1/1) running `it_works`... 173 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:33 174 | all tests passed! 175 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:28 176 | ``` 177 | 178 | Integration tests reside in the `tests` directory; the initial set of integration tests are in `tests/integration.rs`. 179 | `cargo test --test integration` will run those integration tests. 180 | Note that the argument of the `--test` flag must match the name of the test file in the `tests` directory. 181 | 182 | ``` console 183 | $ cargo test --test integration 184 | (1/1) running `it_works`... 185 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:13 186 | all tests passed! 187 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:8 188 | ``` 189 | 190 | Note that to add a new test file to the `tests` directory you also need to add a new `[[test]]` section to `Cargo.toml`. 191 | 192 | ## Trying out the git version of defmt 193 | 194 | This template is configured to use the latest crates.io release (the "stable" release) of the `defmt` framework. 195 | To use the git version (the "development" version) of `defmt` follow these steps: 196 | 197 | 1. Install the *git* version of `probe-run` 198 | 199 | ``` console 200 | $ cargo install --git https://github.com/knurling-rs/probe-run --branch main 201 | ``` 202 | 203 | 2. Check which defmt version `probe-run` supports 204 | 205 | ``` console 206 | $ probe-run --version 207 | 0.2.0 (aa585f2 2021-02-22) 208 | supported defmt version: 60c6447f8ecbc4ff023378ba6905bcd0de1e679f 209 | ``` 210 | 211 | In the example output, the supported version is `60c6447f8ecbc4ff023378ba6905bcd0de1e679f` 212 | 213 | 3. Switch defmt dependencies to git: uncomment the last part of the root `Cargo.toml` and enter the hash reported by `probe-run --version`: 214 | 215 | ``` diff 216 | -# [patch.crates-io] 217 | -# defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 218 | -# defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 219 | -# defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 220 | -# panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 221 | +[patch.crates-io] 222 | +defmt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 223 | +defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 224 | +defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 225 | +panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 226 | ``` 227 | 228 | You are now using the git version of `defmt`! 229 | 230 | **NOTE** there may have been breaking changes between the crates.io version and the git version; you'll need to fix those in the source code. 231 | 232 | ## Support 233 | 234 | `app-template` is part of the [Knurling] project, [Ferrous Systems]' effort at 235 | improving tooling used to develop for embedded systems. 236 | 237 | If you think that our work is useful, consider sponsoring it via [GitHub 238 | Sponsors]. 239 | 240 | ## License 241 | 242 | Licensed under either of 243 | 244 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 245 | http://www.apache.org/licenses/LICENSE-2.0) 246 | 247 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 248 | 249 | at your option. 250 | 251 | ### Contribution 252 | 253 | Unless you explicitly state otherwise, any contribution intentionally submitted 254 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 255 | licensed as above, without any additional terms or conditions. 256 | 257 | [Knurling]: https://knurling.ferrous-systems.com 258 | [Ferrous Systems]: https://ferrous-systems.com/ 259 | [GitHub Sponsors]: https://github.com/sponsors/knurling-rs 260 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/memory.x: -------------------------------------------------------------------------------- 1 | 2 | 3 | MEMORY 4 | { 5 | /* NOTE K = KiBi = 1024 bytes */ 6 | FLASH : ORIGIN = 0x08000000, LENGTH = 512K 7 | RAM : ORIGIN = 0x20000000, LENGTH = 128K 8 | } 9 | 10 | /* This is where the call stack will be allocated. */ 11 | /* The stack is of the full descending type. */ 12 | /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ 13 | _stack_start = ORIGIN(RAM) + LENGTH(RAM); -------------------------------------------------------------------------------- /firmware/blackpill-phm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | use defmt_rtt as _; // global logger 6 | use panic_probe as _; 7 | use stm32f4xx_hal as _; // memory layout 8 | 9 | // same panicking *behavior* as `panic-probe` but doesn't print a panic message 10 | // this prevents the panic message being printed *twice* when `defmt::panic` is invoked 11 | #[defmt::panic_handler] 12 | fn panic() -> ! { 13 | cortex_m::asm::udf() 14 | } 15 | 16 | static COUNT: AtomicUsize = AtomicUsize::new(0); 17 | defmt::timestamp!("{=usize}", { 18 | // NOTE(no-CAS) `timestamps` runs with interrupts disabled 19 | let n = COUNT.load(Ordering::Relaxed); 20 | COUNT.store(n + 1, Ordering::Relaxed); 21 | n 22 | }); 23 | 24 | /// Terminates the application and makes `probe-run` exit with exit-code = 0 25 | pub fn exit() -> ! { 26 | loop { 27 | cortex_m::asm::bkpt(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use blackpill_phm as _; // global logger + panicking-behavior + memory layout 5 | 6 | #[rtic::app(device = stm32f4xx_hal::pac, dispatchers = [USART1])] 7 | mod app { 8 | use defmt::unwrap; 9 | use heapless::spsc::Queue; 10 | use phm_icd::{ToMcu, ToPc}; 11 | use phm_worker::{ 12 | comms::{CommsLink, InterfaceComms, WorkerComms}, 13 | Worker, 14 | }; 15 | use postcard::{to_vec_cobs, CobsAccumulator, FeedResult}; 16 | use stm32f4xx_hal::{ 17 | gpio::{ 18 | gpioa::{PA2, PA3, PA5, PA6, PA7}, 19 | gpiob::{PB8, PB9}, 20 | Alternate, OpenDrain, PushPull, 21 | }, 22 | i2c::I2c, 23 | otg_fs::{UsbBus, UsbBusType, USB}, 24 | pac::{I2C1, SPI1, USART2}, 25 | prelude::*, 26 | serial::{config::Config as UartConfig, Serial}, 27 | spi::{Mode, Phase, Polarity, Spi, TransferModeNormal}, 28 | timer::{ 29 | monotonic::{ExtU32, MonoTimer}, 30 | Timer, 31 | }, 32 | }; 33 | use usb_device::{ 34 | class_prelude::UsbBusAllocator, 35 | device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, 36 | }; 37 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 38 | type PhmI2c = I2c>, PB9>)>; 39 | type PhmUart = Serial>, PA3>), u8>; 40 | type PhmSpi = Spi< 41 | SPI1, 42 | ( 43 | PA5>, 44 | PA6>, 45 | PA7>, 46 | ), 47 | TransferModeNormal, 48 | >; 49 | 50 | #[monotonic(binds = TIM2, default = true)] 51 | type Monotonic = MonoTimer; 52 | 53 | #[shared] 54 | struct Shared {} 55 | 56 | #[local] 57 | struct Local { 58 | interface_comms: InterfaceComms<8>, 59 | worker: Worker, PhmI2c, PhmSpi, PhmUart>, 60 | usb_serial: SerialPort<'static, UsbBus>, 61 | usb_dev: UsbDevice<'static, UsbBus>, 62 | } 63 | 64 | #[init(local = [ 65 | ep_memory: [u32; 1024] = [0; 1024], 66 | usb_bus: Option> = None, 67 | incoming: Queue = Queue::new(), 68 | outgoing: Queue, 8> = Queue::new(), 69 | ])] 70 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 71 | let device = cx.device; 72 | 73 | // Set up the system clocks 74 | let rcc = device.RCC.constrain(); 75 | let clocks = rcc.cfgr.sysclk(48.mhz()).require_pll48clk().freeze(); 76 | 77 | // Configure the monotonic timer, currently using TIMER0, a 32-bit, 1MHz timer 78 | let mono = Timer::new(device.TIM2, &clocks).monotonic(); 79 | 80 | // Create GPIO ports for pin-mapping 81 | let gpioa = device.GPIOA.split(); 82 | let gpiob = device.GPIOB.split(); 83 | 84 | // Set up I2C 85 | let scl = gpiob.pb8.into_alternate_open_drain(); 86 | let sda = gpiob.pb9.into_alternate_open_drain(); 87 | let i2c = I2c::new(device.I2C1, (scl, sda), 400.khz(), &clocks); 88 | 89 | // Set up SPI 90 | let sck = gpioa.pa5.into_alternate(); 91 | let miso = gpioa.pa6.into_alternate(); 92 | let mosi = gpioa.pa7.into_alternate(); 93 | let spi = Spi::new( 94 | device.SPI1, 95 | (sck, miso, mosi), 96 | Mode { 97 | polarity: Polarity::IdleLow, 98 | phase: Phase::CaptureOnFirstTransition, 99 | }, 100 | 2_000.khz(), 101 | &clocks, 102 | ); 103 | 104 | // define RX/TX pins 105 | let tx_pin = gpioa.pa2.into_alternate(); 106 | let rx_pin = gpioa.pa3.into_alternate(); 107 | // configure serial 108 | let uart = Serial::new( 109 | device.USART2, 110 | (tx_pin, rx_pin), 111 | UartConfig::default().baudrate(9600.bps()), 112 | &clocks, 113 | ) 114 | .unwrap(); 115 | 116 | // Set up USB 117 | let usb = USB { 118 | usb_global: device.OTG_FS_GLOBAL, 119 | usb_device: device.OTG_FS_DEVICE, 120 | usb_pwrclk: device.OTG_FS_PWRCLK, 121 | pin_dm: gpioa.pa11.into_alternate(), 122 | pin_dp: gpioa.pa12.into_alternate(), 123 | hclk: clocks.hclk(), 124 | }; 125 | let usb_bus = cx.local.usb_bus; 126 | usb_bus.replace(UsbBus::new(usb, cx.local.ep_memory)); 127 | 128 | // Set up USB Serial Port 129 | let usb_serial = SerialPort::new(usb_bus.as_ref().unwrap()); 130 | let usb_dev = UsbDeviceBuilder::new(usb_bus.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 131 | .manufacturer("OVAR Labs") 132 | .product("PHM Worker") 133 | // TODO: Use some kind of unique ID. This will probably require another singleton, 134 | // as the storage must be static. Probably heapless::String -> singleton!() 135 | .serial_number("ajm123") 136 | .device_class(USB_CLASS_CDC) 137 | .max_packet_size_0(64) // (makes control transfers 8x faster) 138 | .build(); 139 | 140 | let comms = CommsLink { 141 | to_pc: cx.local.outgoing, 142 | to_mcu: cx.local.incoming, 143 | }; 144 | 145 | let (worker_comms, interface_comms) = comms.split(); 146 | 147 | let worker = Worker::new(worker_comms, i2c, spi, uart); 148 | usb_tick::spawn().ok(); 149 | ( 150 | Shared {}, 151 | Local { 152 | worker, 153 | interface_comms, 154 | usb_serial, 155 | usb_dev, 156 | }, 157 | init::Monotonics(mono), 158 | ) 159 | } 160 | 161 | #[task(local = [usb_serial, interface_comms, usb_dev, cobs_buf: CobsAccumulator<512> = CobsAccumulator::new()])] 162 | fn usb_tick(cx: usb_tick::Context) { 163 | let usb_serial = cx.local.usb_serial; 164 | let usb_dev = cx.local.usb_dev; 165 | let cobs_buf = cx.local.cobs_buf; 166 | let interface_comms = cx.local.interface_comms; 167 | 168 | let mut buf = [0u8; 128]; 169 | 170 | usb_dev.poll(&mut [usb_serial]); 171 | 172 | if let Some(out) = interface_comms.to_pc.dequeue() { 173 | if let Ok(ser_msg) = to_vec_cobs::<_, 128>(&out) { 174 | usb_serial.write(&ser_msg).ok(); 175 | } else { 176 | defmt::panic!("Serialization error!"); 177 | } 178 | } 179 | 180 | match usb_serial.read(&mut buf) { 181 | Ok(sz) if sz > 0 => { 182 | let buf = &buf[..sz]; 183 | let mut window = &buf[..]; 184 | 185 | 'cobs: while !window.is_empty() { 186 | window = match cobs_buf.feed::(&window) { 187 | FeedResult::Consumed => break 'cobs, 188 | FeedResult::OverFull(new_wind) => new_wind, 189 | FeedResult::DeserError(new_wind) => new_wind, 190 | FeedResult::Success { data, remaining } => { 191 | defmt::println!("got: {:?}", data); 192 | interface_comms.to_mcu.enqueue(data).ok(); 193 | remaining 194 | } 195 | }; 196 | } 197 | } 198 | Ok(_) | Err(usb_device::UsbError::WouldBlock) => {} 199 | Err(_e) => defmt::panic!("Usb Error!"), 200 | } 201 | usb_tick::spawn_after(1.millis()).ok(); 202 | } 203 | 204 | #[idle(local = [worker])] 205 | fn idle(cx: idle::Context) -> ! { 206 | defmt::println!("Hello, world!"); 207 | let worker = cx.local.worker; 208 | 209 | loop { 210 | unwrap!(worker.step().map_err(drop)); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /firmware/blackpill-phm/tests/integration.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use nrf52_phm as _; // memory layout + panic handler 5 | 6 | // See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' 7 | // feature) 8 | #[defmt_test::tests] 9 | mod tests { 10 | use defmt::assert; 11 | 12 | #[test] 13 | fn it_works() { 14 | assert!(true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 2 | runner = "probe-run --chip nRF52840_xxAA" 3 | rustflags = [ 4 | "-C", "linker=flip-link", 5 | "-C", "link-arg=-Tlink.x", 6 | "-C", "link-arg=-Tdefmt.x", 7 | # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x 8 | # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 9 | "-C", "link-arg=--nmagic", 10 | ] 11 | 12 | [build] 13 | # (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right 14 | # target improves performance) 15 | # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 16 | # target = "thumbv7m-none-eabi" # Cortex-M3 17 | # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 18 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 19 | 20 | [alias] 21 | rb = "run --bin" 22 | rrb = "run --release --bin" 23 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // override the default setting (`cargo check --all-targets`) which produces the following error 3 | // "can't find crate for `test`" when the default compilation target is a no_std target 4 | // with these changes RA will call `cargo check --bins` on save 5 | "rust-analyzer.checkOnSave.allTargets": false, 6 | "rust-analyzer.checkOnSave.extraArgs": [ 7 | "--bins" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["James Munns ", "Henrik Alsér "] 3 | name = "nrf52-phm" 4 | edition = "2021" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | harness = false 9 | 10 | # needed for each integration test 11 | [[test]] 12 | name = "integration" 13 | harness = false 14 | 15 | [dependencies] 16 | cortex-m = "0.7.3" 17 | cortex-m-rt = "0.7.0" 18 | cortex-m-rtic = "1.0.0" 19 | rtic-monotonic = "1.0.0" 20 | fugit = "0.3.3" 21 | defmt = "0.3.0" 22 | defmt-rtt = "0.3.0" 23 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } 24 | nrf52840-hal = "0.14.1" 25 | usb-device = "0.2.8" 26 | usbd-serial = "0.1.1" 27 | postcard = "0.7.2" 28 | embedded-hal = "0.2.6" 29 | nb = "1.0.0" 30 | 31 | [dependencies.heapless] 32 | version = "0.7.10" 33 | features = ["serde"] 34 | 35 | [dependencies.phm-icd] 36 | path = "../../common/phm-icd" 37 | features = ["use-defmt"] 38 | 39 | [dependencies.phm-worker] 40 | path = "../phm-worker" 41 | 42 | [dev-dependencies] 43 | defmt-test = "0.3.0" 44 | 45 | # cargo build/run 46 | [profile.dev] 47 | codegen-units = 1 48 | debug = 2 49 | debug-assertions = true # <- 50 | incremental = false 51 | opt-level = 3 # <- 52 | overflow-checks = true # <- 53 | 54 | # cargo test 55 | [profile.test] 56 | codegen-units = 1 57 | debug = 2 58 | debug-assertions = true # <- 59 | incremental = false 60 | opt-level = 3 # <- 61 | overflow-checks = true # <- 62 | 63 | # cargo build/run --release 64 | [profile.release] 65 | codegen-units = 1 66 | debug = 2 67 | debug-assertions = false # <- 68 | incremental = false 69 | # NOTE disabled to work around issue rust-lang/rust#90357 70 | # the bug results in log messages not having location information 71 | # (the line printed below the log message that contains the file-line location) 72 | # lto = 'fat' 73 | opt-level = 3 # <- 74 | overflow-checks = false # <- 75 | 76 | # cargo test --release 77 | [profile.bench] 78 | codegen-units = 1 79 | debug = 2 80 | debug-assertions = false # <- 81 | incremental = false 82 | # see comment in the profile.release section 83 | lto = 'false' 84 | opt-level = 3 # <- 85 | overflow-checks = false # <- 86 | 87 | # uncomment this to switch from the crates.io version of defmt to its git version 88 | # check app-template's README for instructions 89 | # [patch.crates-io] 90 | # defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 91 | # defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 92 | # defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 93 | # panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 94 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/README.md: -------------------------------------------------------------------------------- 1 | # `app-template` 2 | 3 | > Quickly set up a [`probe-run`] + [`defmt`] + [`flip-link`] embedded project 4 | 5 | [`probe-run`]: https://crates.io/crates/probe-run 6 | [`defmt`]: https://github.com/knurling-rs/defmt 7 | [`flip-link`]: https://github.com/knurling-rs/flip-link 8 | 9 | ## Dependencies 10 | 11 | #### 1. `flip-link`: 12 | 13 | ```console 14 | $ cargo install flip-link 15 | ``` 16 | 17 | #### 2. `probe-run`: 18 | 19 | ``` console 20 | $ # make sure to install v0.2.0 or later 21 | $ cargo install probe-run 22 | ``` 23 | 24 | #### 3. [`cargo-generate`]: 25 | 26 | ``` console 27 | $ cargo install cargo-generate 28 | ``` 29 | 30 | [`cargo-generate`]: https://crates.io/crates/cargo-generate 31 | 32 | > *Note:* You can also just clone this repository instead of using `cargo-generate`, but this involves additional manual adjustments. 33 | 34 | ## Setup 35 | 36 | #### 1. Initialize the project template 37 | 38 | ``` console 39 | $ cargo generate \ 40 | --git https://github.com/knurling-rs/app-template \ 41 | --branch main \ 42 | --name my-app 43 | ``` 44 | 45 | If you look into your new `my-app` folder, you'll find that there are a few `TODO`s in the files marking the properties you need to set. 46 | 47 | Let's walk through them together now. 48 | 49 | #### 2. Set `probe-run` chip 50 | 51 | Pick a chip from `probe-run --list-chips` and enter it into `.cargo/config.toml`. 52 | 53 | If, for example, you have a nRF52840 Development Kit from one of [our workshops], replace `{{chip}}` with `nRF52840_xxAA`. 54 | 55 | [our workshops]: https://github.com/ferrous-systems/embedded-trainings-2020 56 | 57 | ``` diff 58 | # .cargo/config.toml 59 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 60 | -runner = "probe-run --chip {{chip}}" 61 | +runner = "probe-run --chip nRF52840_xxAA" 62 | ``` 63 | 64 | #### 3. Adjust the compilation target 65 | 66 | In `.cargo/config.toml`, pick the right compilation target for your board. 67 | 68 | ``` diff 69 | # .cargo/config.toml 70 | [build] 71 | -target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 72 | -# target = "thumbv7m-none-eabi" # Cortex-M3 73 | -# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 74 | -# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 75 | +target = "thumbv7em-none-eabihf" # Cortex-M4F (with FPU) 76 | ``` 77 | 78 | Add the target with `rustup`. 79 | 80 | ``` console 81 | $ rustup target add thumbv7em-none-eabihf 82 | ``` 83 | 84 | #### 4. Add a HAL as a dependency 85 | 86 | In `Cargo.toml`, list the Hardware Abstraction Layer (HAL) for your board as a dependency. 87 | 88 | For the nRF52840 you'll want to use the [`nrf52840-hal`]. 89 | 90 | [`nrf52840-hal`]: https://crates.io/crates/nrf52840-hal 91 | 92 | ``` diff 93 | # Cargo.toml 94 | [dependencies] 95 | -# some-hal = "1.2.3" 96 | +nrf52840-hal = "0.14.0" 97 | ``` 98 | 99 | #### 5. Import your HAL 100 | 101 | Now that you have selected a HAL, fix the HAL import in `src/lib.rs` 102 | 103 | ``` diff 104 | // my-app/src/lib.rs 105 | -// use some_hal as _; // memory layout 106 | +use nrf52840_hal as _; // memory layout 107 | ``` 108 | 109 | #### (6. Get a linker script) 110 | 111 | Some HAL crates require that you manually copy over a file called `memory.x` from the HAL to the root of your project. For nrf52840-hal, this is done automatically so no action is needed. For other HAL crates, you can get it from your local Cargo folder, the default location is under: 112 | 113 | ``` 114 | ~/.cargo/registry/src/ 115 | ``` 116 | 117 | Not all HALs provide a `memory.x` file, you may need to write it yourself. Check the documentation for the HAL you are using. 118 | 119 | 120 | #### 7. Run! 121 | 122 | You are now all set to `cargo-run` your first `defmt`-powered application! 123 | There are some examples in the `src/bin` directory. 124 | 125 | Start by `cargo run`-ning `my-app/src/bin/hello.rs`: 126 | 127 | ``` console 128 | $ # `rb` is an alias for `run --bin` 129 | $ cargo rb hello 130 | Finished dev [optimized + debuginfo] target(s) in 0.03s 131 | flashing program .. 132 | DONE 133 | resetting device 134 | 0.000000 INFO Hello, world! 135 | (..) 136 | 137 | $ echo $? 138 | 0 139 | ``` 140 | 141 | If you're running out of memory (`flip-link` bails with an overflow error), you can decrease the size of the device memory buffer by setting the `DEFMT_RTT_BUFFER_SIZE` environment variable. The default value is 1024 bytes, and powers of two should be used for optimal performance: 142 | 143 | ``` console 144 | $ DEFMT_RTT_BUFFER_SIZE=64 cargo rb hello 145 | ``` 146 | 147 | #### (8. Set `rust-analyzer.linkedProjects`) 148 | 149 | If you are using [rust-analyzer] with VS Code for IDE-like features you can add following configuration to your `.vscode/settings.json` to make it work transparently across workspaces. Find the details of this option in the [RA docs]. 150 | 151 | ```json 152 | { 153 | "rust-analyzer.linkedProjects": [ 154 | "Cargo.toml", 155 | "firmware/Cargo.toml", 156 | ] 157 | } 158 | ``` 159 | 160 | [RA docs]: https://rust-analyzer.github.io/manual.html#configuration 161 | [rust-analyzer]: https://rust-analyzer.github.io/ 162 | 163 | ## Running tests 164 | 165 | The template comes configured for running unit tests and integration tests on the target. 166 | 167 | Unit tests reside in the library crate and can test private API; the initial set of unit tests are in `src/lib.rs`. 168 | `cargo test --lib` will run those unit tests. 169 | 170 | ``` console 171 | $ cargo test --lib 172 | (1/1) running `it_works`... 173 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:33 174 | all tests passed! 175 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:28 176 | ``` 177 | 178 | Integration tests reside in the `tests` directory; the initial set of integration tests are in `tests/integration.rs`. 179 | `cargo test --test integration` will run those integration tests. 180 | Note that the argument of the `--test` flag must match the name of the test file in the `tests` directory. 181 | 182 | ``` console 183 | $ cargo test --test integration 184 | (1/1) running `it_works`... 185 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:13 186 | all tests passed! 187 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:8 188 | ``` 189 | 190 | Note that to add a new test file to the `tests` directory you also need to add a new `[[test]]` section to `Cargo.toml`. 191 | 192 | ## Trying out the git version of defmt 193 | 194 | This template is configured to use the latest crates.io release (the "stable" release) of the `defmt` framework. 195 | To use the git version (the "development" version) of `defmt` follow these steps: 196 | 197 | 1. Install the *git* version of `probe-run` 198 | 199 | ``` console 200 | $ cargo install --git https://github.com/knurling-rs/probe-run --branch main 201 | ``` 202 | 203 | 2. Check which defmt version `probe-run` supports 204 | 205 | ``` console 206 | $ probe-run --version 207 | 0.2.0 (aa585f2 2021-02-22) 208 | supported defmt version: 60c6447f8ecbc4ff023378ba6905bcd0de1e679f 209 | ``` 210 | 211 | In the example output, the supported version is `60c6447f8ecbc4ff023378ba6905bcd0de1e679f` 212 | 213 | 3. Switch defmt dependencies to git: uncomment the last part of the root `Cargo.toml` and enter the hash reported by `probe-run --version`: 214 | 215 | ``` diff 216 | -# [patch.crates-io] 217 | -# defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 218 | -# defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 219 | -# defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 220 | -# panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 221 | +[patch.crates-io] 222 | +defmt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 223 | +defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 224 | +defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 225 | +panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 226 | ``` 227 | 228 | You are now using the git version of `defmt`! 229 | 230 | **NOTE** there may have been breaking changes between the crates.io version and the git version; you'll need to fix those in the source code. 231 | 232 | ## Support 233 | 234 | `app-template` is part of the [Knurling] project, [Ferrous Systems]' effort at 235 | improving tooling used to develop for embedded systems. 236 | 237 | If you think that our work is useful, consider sponsoring it via [GitHub 238 | Sponsors]. 239 | 240 | ## License 241 | 242 | Licensed under either of 243 | 244 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 245 | http://www.apache.org/licenses/LICENSE-2.0) 246 | 247 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 248 | 249 | at your option. 250 | 251 | ### Contribution 252 | 253 | Unless you explicitly state otherwise, any contribution intentionally submitted 254 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 255 | licensed as above, without any additional terms or conditions. 256 | 257 | [Knurling]: https://knurling.ferrous-systems.com 258 | [Ferrous Systems]: https://ferrous-systems.com/ 259 | [GitHub Sponsors]: https://github.com/sponsors/knurling-rs 260 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | pub mod monotonic; 5 | pub mod uart; 6 | 7 | use defmt_rtt as _; // global logger 8 | 9 | use nrf52840_hal as _; // memory layout 10 | 11 | use panic_probe as _; 12 | 13 | // same panicking *behavior* as `panic-probe` but doesn't print a panic message 14 | // this prevents the panic message being printed *twice* when `defmt::panic` is invoked 15 | #[defmt::panic_handler] 16 | fn panic() -> ! { 17 | cortex_m::asm::udf() 18 | } 19 | 20 | /// Terminates the application and makes `probe-run` exit with exit-code = 0 21 | pub fn exit() -> ! { 22 | loop { 23 | cortex_m::asm::bkpt(); 24 | } 25 | } 26 | 27 | // defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used 28 | // once within a crate. the module can be in any file but there can only be at most 29 | // one `#[tests]` module in this library crate 30 | #[cfg(test)] 31 | #[defmt_test::tests] 32 | mod unit_tests { 33 | use defmt::assert; 34 | 35 | #[test] 36 | fn it_works() { 37 | assert!(true) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use nrf52_phm as _; // global logger + panicking-behavior + memory layout 5 | 6 | #[rtic::app(device = nrf52840_hal::pac, dispatchers = [SWI0_EGU0])] 7 | mod app { 8 | use cortex_m::singleton; 9 | use defmt::unwrap; 10 | use heapless::spsc::Queue; 11 | use nrf52840_hal::{ 12 | clocks::{ExternalOscillator, Internal, LfOscStopped}, 13 | gpio::{p0::Parts as P0Parts, p1::Parts as P1Parts, Level}, 14 | pac::{SPIM2, TIMER0, TWIM0}, 15 | spim::{Frequency as SpimFreq, Pins as SpimPins, Spim, MODE_0}, 16 | twim::{Frequency as TwimFreq, Pins as TwimPins, Twim}, 17 | uarte::{Baudrate, Parity, Pins as UartPins, Uarte}, 18 | usbd::{UsbPeripheral, Usbd}, 19 | Clocks, 20 | }; 21 | use nrf52_phm::monotonic::{ExtU32, MonoTimer}; 22 | use nrf52_phm::uart::PhmUart; 23 | use phm_icd::{ToMcu, ToPc}; 24 | use phm_worker::{ 25 | comms::{CommsLink, InterfaceComms, WorkerComms}, 26 | Worker, 27 | }; 28 | use postcard::{to_vec_cobs, CobsAccumulator, FeedResult}; 29 | use usb_device::{ 30 | class_prelude::UsbBusAllocator, 31 | device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, 32 | }; 33 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 34 | 35 | #[monotonic(binds = TIMER0, default = true)] 36 | type Monotonic = MonoTimer; 37 | 38 | #[shared] 39 | struct Shared {} 40 | 41 | #[local] 42 | struct Local { 43 | interface_comms: InterfaceComms<8>, 44 | worker: Worker, Twim, Spim, PhmUart>, 45 | usb_serial: SerialPort<'static, Usbd>>, 46 | usb_dev: UsbDevice<'static, Usbd>>, 47 | } 48 | 49 | #[init(local = [ 50 | usb_bus: Option>>> = None, 51 | incoming: Queue = Queue::new(), 52 | outgoing: Queue, 8> = Queue::new(), 53 | uart_rx_buf: [u8; 64] = [0; 64], 54 | uart_tx_buf: [u8; 1] = [0], 55 | ])] 56 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 57 | let device = cx.device; 58 | 59 | // Setup clocks early in the process. We need this for USB later 60 | let clocks = Clocks::new(device.CLOCK); 61 | let clocks = clocks.enable_ext_hfosc(); 62 | let clocks = 63 | unwrap!(singleton!(: Clocks = clocks)); 64 | 65 | // Configure the monotonic timer, currently using TIMER0, a 32-bit, 1MHz timer 66 | let mono = Monotonic::new(device.TIMER0); 67 | 68 | // Create GPIO ports for pin-mapping 69 | let port0 = P0Parts::new(device.P0); 70 | let port1 = P1Parts::new(device.P1); 71 | 72 | // Set up Twim 73 | let i2c = Twim::new( 74 | device.TWIM0, 75 | TwimPins { 76 | scl: port1.p1_01.into_floating_input().degrade(), 77 | sda: port1.p1_02.into_floating_input().degrade(), 78 | }, 79 | TwimFreq::K100, 80 | ); 81 | 82 | // Set up Spim 83 | let sck = port0.p0_08.into_push_pull_output(Level::Low).degrade(); 84 | let mosi = port0.p0_04.into_push_pull_output(Level::Low).degrade(); 85 | let miso = port0.p0_06.into_floating_input().degrade(); 86 | let spi = Spim::new( 87 | device.SPIM2, 88 | SpimPins { 89 | sck, 90 | miso: Some(miso), 91 | mosi: Some(mosi), 92 | }, 93 | SpimFreq::M2, 94 | MODE_0, 95 | 0, 96 | ); 97 | 98 | // Set up UART 99 | let rxd = port0.p0_28.into_floating_input().degrade(); 100 | let txd = port0.p0_29.into_push_pull_output(Level::High).degrade(); 101 | let pins = UartPins { 102 | rxd, 103 | txd, 104 | cts: None, 105 | rts: None, 106 | }; 107 | let uarte = Uarte::new(device.UARTE0, pins, Parity::EXCLUDED, Baudrate::BAUD9600); 108 | let (tx, rx) = uarte 109 | .split(cx.local.uart_rx_buf, cx.local.uart_tx_buf) 110 | .unwrap(); 111 | let uart = PhmUart { rx, tx }; 112 | 113 | // Set up USB Serial Port 114 | let usb_bus = cx.local.usb_bus; 115 | usb_bus.replace(Usbd::new(UsbPeripheral::new(device.USBD, clocks))); 116 | let usb_serial = SerialPort::new(usb_bus.as_ref().unwrap()); 117 | let usb_dev = UsbDeviceBuilder::new(usb_bus.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 118 | .manufacturer("OVAR Labs") 119 | .product("PHM Worker") 120 | // TODO: Use some kind of unique ID. This will probably require another singleton, 121 | // as the storage must be static. Probably heapless::String -> singleton!() 122 | .serial_number("ajm123") 123 | .device_class(USB_CLASS_CDC) 124 | .max_packet_size_0(64) // (makes control transfers 8x faster) 125 | .build(); 126 | 127 | let comms = CommsLink { 128 | to_pc: cx.local.outgoing, 129 | to_mcu: cx.local.incoming, 130 | }; 131 | 132 | let (worker_comms, interface_comms) = comms.split(); 133 | 134 | let worker = Worker::new(worker_comms, i2c, spi, uart); 135 | 136 | usb_tick::spawn().ok(); 137 | ( 138 | Shared {}, 139 | Local { 140 | worker, 141 | interface_comms, 142 | usb_serial, 143 | usb_dev, 144 | }, 145 | init::Monotonics(mono), 146 | ) 147 | } 148 | 149 | #[task(local = [usb_serial, interface_comms, usb_dev, cobs_buf: CobsAccumulator<512> = CobsAccumulator::new()])] 150 | fn usb_tick(cx: usb_tick::Context) { 151 | let usb_serial = cx.local.usb_serial; 152 | let usb_dev = cx.local.usb_dev; 153 | let cobs_buf = cx.local.cobs_buf; 154 | let interface_comms = cx.local.interface_comms; 155 | 156 | let mut buf = [0u8; 128]; 157 | 158 | usb_dev.poll(&mut [usb_serial]); 159 | 160 | if let Some(out) = interface_comms.to_pc.dequeue() { 161 | if let Ok(ser_msg) = to_vec_cobs::<_, 128>(&out) { 162 | usb_serial.write(&ser_msg).ok(); 163 | } else { 164 | defmt::panic!("Serialization error!"); 165 | } 166 | } 167 | 168 | match usb_serial.read(&mut buf) { 169 | Ok(sz) if sz > 0 => { 170 | let buf = &buf[..sz]; 171 | let mut window = &buf[..]; 172 | 173 | 'cobs: while !window.is_empty() { 174 | window = match cobs_buf.feed::(&window) { 175 | FeedResult::Consumed => break 'cobs, 176 | FeedResult::OverFull(new_wind) => new_wind, 177 | FeedResult::DeserError(new_wind) => new_wind, 178 | FeedResult::Success { data, remaining } => { 179 | defmt::println!("got: {:?}", data); 180 | interface_comms.to_mcu.enqueue(data).ok(); 181 | remaining 182 | } 183 | }; 184 | } 185 | } 186 | Ok(_) | Err(usb_device::UsbError::WouldBlock) => {} 187 | Err(_e) => defmt::panic!("Usb Error!"), 188 | } 189 | 190 | usb_tick::spawn_after(1.millis()).ok(); 191 | } 192 | 193 | #[idle(local = [worker])] 194 | fn idle(cx: idle::Context) -> ! { 195 | defmt::println!("Hello, world!"); 196 | let worker = cx.local.worker; 197 | 198 | loop { 199 | unwrap!(worker.step().map_err(drop)); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/src/monotonic.rs: -------------------------------------------------------------------------------- 1 | // RTIC Monotonic impl for the 32-bit timers 2 | pub use fugit::{self, ExtU32}; 3 | use nrf52840_hal::pac::{timer0, TIMER0, TIMER1, TIMER2}; 4 | use rtic_monotonic::Monotonic; 5 | 6 | pub struct MonoTimer(T); 7 | 8 | impl MonoTimer { 9 | pub fn new(timer: T) -> Self { 10 | timer.prescaler.write( 11 | |w| unsafe { w.prescaler().bits(4) }, // 1 MHz 12 | ); 13 | timer.bitmode.write(|w| w.bitmode()._32bit()); 14 | MonoTimer(timer) 15 | } 16 | } 17 | 18 | impl Monotonic for MonoTimer { 19 | type Instant = fugit::TimerInstantU32<1_000_000>; 20 | type Duration = fugit::TimerDurationU32<1_000_000>; 21 | 22 | unsafe fn reset(&mut self) { 23 | self.0.intenset.modify(|_, w| w.compare0().set()); 24 | self.0.tasks_clear.write(|w| w.bits(1)); 25 | self.0.tasks_start.write(|w| w.bits(1)); 26 | } 27 | 28 | #[inline(always)] 29 | fn now(&mut self) -> Self::Instant { 30 | self.0.tasks_capture[1].write(|w| unsafe { w.bits(1) }); 31 | Self::Instant::from_ticks(self.0.cc[1].read().bits()) 32 | } 33 | 34 | fn set_compare(&mut self, instant: Self::Instant) { 35 | self.0.cc[0].write(|w| unsafe { w.cc().bits(instant.duration_since_epoch().ticks()) }); 36 | } 37 | 38 | fn clear_compare_flag(&mut self) { 39 | self.0.events_compare[0].write(|w| w); 40 | } 41 | 42 | #[inline(always)] 43 | fn zero() -> Self::Instant { 44 | Self::Instant::from_ticks(0) 45 | } 46 | } 47 | 48 | pub trait Instance32: core::ops::Deref {} 49 | impl Instance32 for TIMER0 {} 50 | impl Instance32 for TIMER1 {} 51 | impl Instance32 for TIMER2 {} 52 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/src/uart.rs: -------------------------------------------------------------------------------- 1 | use nrf52840_hal::{ 2 | pac::UARTE0, 3 | uarte::{UarteRx, UarteTx}, 4 | }; 5 | 6 | pub struct PhmUart { 7 | pub rx: UarteRx, 8 | pub tx: UarteTx, 9 | } 10 | 11 | impl embedded_hal::serial::Read for PhmUart { 12 | type Error = (); 13 | 14 | fn read(&mut self) -> Result> { 15 | embedded_hal::serial::Read::::read(&mut self.rx).map_err(|_| nb::Error::WouldBlock) 16 | } 17 | } 18 | 19 | impl embedded_hal::serial::Write for PhmUart { 20 | type Error = (); 21 | 22 | fn write(&mut self, output: u8) -> Result<(), nb::Error> { 23 | embedded_hal::serial::Write::::write(&mut self.tx, output) 24 | .map_err(|_| nb::Error::WouldBlock) 25 | } 26 | 27 | fn flush(&mut self) -> Result<(), nb::Error> { 28 | embedded_hal::serial::Write::::flush(&mut self.tx).map_err(|_| nb::Error::WouldBlock) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /firmware/nrf52-phm/tests/integration.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use nrf52_phm as _; // memory layout + panic handler 5 | 6 | // See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' 7 | // feature) 8 | #[defmt_test::tests] 9 | mod tests { 10 | use defmt::assert; 11 | 12 | #[test] 13 | fn it_works() { 14 | assert!(true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /firmware/phm-worker/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "atomic-polyfill" 16 | version = "0.1.5" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e686d748538a32325b28d6411dd8a939e7ad5128e5d0023cc4fd3573db456042" 19 | dependencies = [ 20 | "critical-section", 21 | "riscv-target", 22 | ] 23 | 24 | [[package]] 25 | name = "bare-metal" 26 | version = "0.2.5" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 29 | dependencies = [ 30 | "rustc_version", 31 | ] 32 | 33 | [[package]] 34 | name = "bare-metal" 35 | version = "1.0.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 38 | 39 | [[package]] 40 | name = "bit_field" 41 | version = "0.10.1" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 44 | 45 | [[package]] 46 | name = "bitfield" 47 | version = "0.13.2" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 50 | 51 | [[package]] 52 | name = "bitflags" 53 | version = "1.3.2" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 56 | 57 | [[package]] 58 | name = "byteorder" 59 | version = "1.4.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 62 | 63 | [[package]] 64 | name = "cfg-if" 65 | version = "1.0.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 68 | 69 | [[package]] 70 | name = "cortex-m" 71 | version = "0.7.4" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" 74 | dependencies = [ 75 | "bare-metal 0.2.5", 76 | "bitfield", 77 | "embedded-hal", 78 | "volatile-register", 79 | ] 80 | 81 | [[package]] 82 | name = "critical-section" 83 | version = "0.2.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" 86 | dependencies = [ 87 | "bare-metal 1.0.0", 88 | "cfg-if", 89 | "cortex-m", 90 | "riscv", 91 | ] 92 | 93 | [[package]] 94 | name = "defmt" 95 | version = "0.3.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "62fb5df4d2b06a2dbf6ba26b49031f5f45f1aafdfca4b9259719466d362f34a0" 98 | dependencies = [ 99 | "bitflags", 100 | "defmt-macros", 101 | ] 102 | 103 | [[package]] 104 | name = "defmt-macros" 105 | version = "0.3.1" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "098396b763a1786b329405f69bc3677e00d45eb4534bc9f31cd23011ee2ba267" 108 | dependencies = [ 109 | "defmt-parser", 110 | "proc-macro-error", 111 | "proc-macro2", 112 | "quote", 113 | "syn", 114 | ] 115 | 116 | [[package]] 117 | name = "defmt-parser" 118 | version = "0.3.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "8d1ce010e1a51aef925c98f12ed81a4e0e96ce0185a87c33f1b3b9c8f20749c7" 121 | 122 | [[package]] 123 | name = "embedded-hal" 124 | version = "0.2.6" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" 127 | dependencies = [ 128 | "nb 0.1.3", 129 | "void", 130 | ] 131 | 132 | [[package]] 133 | name = "hash32" 134 | version = "0.2.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 137 | dependencies = [ 138 | "byteorder", 139 | ] 140 | 141 | [[package]] 142 | name = "heapless" 143 | version = "0.7.10" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" 146 | dependencies = [ 147 | "atomic-polyfill", 148 | "defmt", 149 | "hash32", 150 | "serde", 151 | "spin", 152 | "stable_deref_trait", 153 | ] 154 | 155 | [[package]] 156 | name = "lazy_static" 157 | version = "1.4.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 160 | 161 | [[package]] 162 | name = "lock_api" 163 | version = "0.4.6" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 166 | dependencies = [ 167 | "scopeguard", 168 | ] 169 | 170 | [[package]] 171 | name = "memchr" 172 | version = "2.4.1" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 175 | 176 | [[package]] 177 | name = "nb" 178 | version = "0.1.3" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 181 | dependencies = [ 182 | "nb 1.0.0", 183 | ] 184 | 185 | [[package]] 186 | name = "nb" 187 | version = "1.0.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 190 | 191 | [[package]] 192 | name = "phm-icd" 193 | version = "0.0.1" 194 | dependencies = [ 195 | "defmt", 196 | "heapless", 197 | "serde", 198 | ] 199 | 200 | [[package]] 201 | name = "phm-worker" 202 | version = "0.1.0" 203 | dependencies = [ 204 | "defmt", 205 | "embedded-hal", 206 | "heapless", 207 | "phm-icd", 208 | ] 209 | 210 | [[package]] 211 | name = "proc-macro-error" 212 | version = "1.0.4" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 215 | dependencies = [ 216 | "proc-macro-error-attr", 217 | "proc-macro2", 218 | "quote", 219 | "syn", 220 | "version_check", 221 | ] 222 | 223 | [[package]] 224 | name = "proc-macro-error-attr" 225 | version = "1.0.4" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 228 | dependencies = [ 229 | "proc-macro2", 230 | "quote", 231 | "version_check", 232 | ] 233 | 234 | [[package]] 235 | name = "proc-macro2" 236 | version = "1.0.36" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 239 | dependencies = [ 240 | "unicode-xid", 241 | ] 242 | 243 | [[package]] 244 | name = "quote" 245 | version = "1.0.15" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 248 | dependencies = [ 249 | "proc-macro2", 250 | ] 251 | 252 | [[package]] 253 | name = "regex" 254 | version = "1.5.4" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 257 | dependencies = [ 258 | "aho-corasick", 259 | "memchr", 260 | "regex-syntax", 261 | ] 262 | 263 | [[package]] 264 | name = "regex-syntax" 265 | version = "0.6.25" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 268 | 269 | [[package]] 270 | name = "riscv" 271 | version = "0.7.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 274 | dependencies = [ 275 | "bare-metal 1.0.0", 276 | "bit_field", 277 | "riscv-target", 278 | ] 279 | 280 | [[package]] 281 | name = "riscv-target" 282 | version = "0.1.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 285 | dependencies = [ 286 | "lazy_static", 287 | "regex", 288 | ] 289 | 290 | [[package]] 291 | name = "rustc_version" 292 | version = "0.2.3" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 295 | dependencies = [ 296 | "semver", 297 | ] 298 | 299 | [[package]] 300 | name = "scopeguard" 301 | version = "1.1.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 304 | 305 | [[package]] 306 | name = "semver" 307 | version = "0.9.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 310 | dependencies = [ 311 | "semver-parser", 312 | ] 313 | 314 | [[package]] 315 | name = "semver-parser" 316 | version = "0.7.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 319 | 320 | [[package]] 321 | name = "serde" 322 | version = "1.0.136" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 325 | dependencies = [ 326 | "serde_derive", 327 | ] 328 | 329 | [[package]] 330 | name = "serde_derive" 331 | version = "1.0.136" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 334 | dependencies = [ 335 | "proc-macro2", 336 | "quote", 337 | "syn", 338 | ] 339 | 340 | [[package]] 341 | name = "spin" 342 | version = "0.9.2" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" 345 | dependencies = [ 346 | "lock_api", 347 | ] 348 | 349 | [[package]] 350 | name = "stable_deref_trait" 351 | version = "1.2.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 354 | 355 | [[package]] 356 | name = "syn" 357 | version = "1.0.86" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 360 | dependencies = [ 361 | "proc-macro2", 362 | "quote", 363 | "unicode-xid", 364 | ] 365 | 366 | [[package]] 367 | name = "unicode-xid" 368 | version = "0.2.2" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 371 | 372 | [[package]] 373 | name = "vcell" 374 | version = "0.1.3" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 377 | 378 | [[package]] 379 | name = "version_check" 380 | version = "0.9.4" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 383 | 384 | [[package]] 385 | name = "void" 386 | version = "1.0.2" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 389 | 390 | [[package]] 391 | name = "volatile-register" 392 | version = "0.2.1" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" 395 | dependencies = [ 396 | "vcell", 397 | ] 398 | -------------------------------------------------------------------------------- /firmware/phm-worker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phm-worker" 3 | version = "0.0.2" 4 | description = "The Embedded Worker for Pretty HAL Machine" 5 | repository = "https://github.com/jamesmunns/pretty-hal-machine" 6 | authors = [ 7 | "James Munns ", 8 | "Henrik Alsér ", 9 | ] 10 | edition = "2021" 11 | readme = "../../README.md" 12 | 13 | categories = [ 14 | "embedded", 15 | ] 16 | license = "MIT OR Apache-2.0" 17 | 18 | [dependencies] 19 | defmt = "0.3.0" 20 | embedded-hal = "0.2.6" 21 | nb = "1.0.0" 22 | 23 | [dependencies.heapless] 24 | version = "0.7.10" 25 | 26 | [dependencies.phm-icd] 27 | version = "0.0.2" 28 | path = "../../common/phm-icd" 29 | features = ["use-defmt"] 30 | -------------------------------------------------------------------------------- /firmware/phm-worker/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Pretty HAL Machine Worker 2 | //! 3 | //! This crate contains the device-agnostic logic that is shared among 4 | //! all implementations of the Pretty HAL Machine worker on different MCUs. 5 | 6 | #![no_std] 7 | 8 | use embedded_hal::blocking::{i2c, spi}; 9 | use embedded_hal::serial; 10 | use phm_icd::{ 11 | Error as IcdError, ToMcu, ToMcuI2c, ToMcuSpi, ToMcuUart, ToPc, ToPcI2c, ToPcSpi, ToPcUart, 12 | }; 13 | 14 | /// The worker Error type 15 | #[derive(Debug, defmt::Format, Eq, PartialEq)] 16 | pub enum Error { 17 | Io, 18 | I2c, 19 | Spi, 20 | Uart, 21 | Internal, 22 | } 23 | 24 | /// Helper types for MCU-to-PC communications 25 | pub mod comms { 26 | use heapless::spsc::{Consumer, Producer, Queue}; 27 | use phm_icd::{Error as IcdError, ToMcu, ToPc}; 28 | 29 | /// A wrapper structure for statically allocated bidirectional queues 30 | pub struct CommsLink { 31 | pub to_pc: &'static mut Queue, N>, 32 | pub to_mcu: &'static mut Queue, 33 | } 34 | 35 | impl CommsLink { 36 | /// Split the CommsLink into Worker and Interface halves. 37 | /// 38 | /// The WorkerComms half is intended to be used with a [Worker](crate::Worker) implmentation, 39 | /// The InterfaceComms half is intended to be used where bytes are send and received to the 40 | /// PC, such as the USB Serial handler function 41 | pub fn split(self) -> (WorkerComms, InterfaceComms) { 42 | let (to_pc_prod, to_pc_cons) = self.to_pc.split(); 43 | let (to_mcu_prod, to_mcu_cons) = self.to_mcu.split(); 44 | 45 | ( 46 | WorkerComms { 47 | to_pc: to_pc_prod, 48 | to_mcu: to_mcu_cons, 49 | }, 50 | InterfaceComms { 51 | to_pc: to_pc_cons, 52 | to_mcu: to_mcu_prod, 53 | }, 54 | ) 55 | } 56 | } 57 | 58 | /// The Worker half of the the CommsLink type. 59 | pub struct WorkerComms { 60 | pub to_pc: Producer<'static, Result, N>, 61 | pub to_mcu: Consumer<'static, ToMcu, N>, 62 | } 63 | 64 | impl crate::WorkerIo for WorkerComms { 65 | type Error = (); 66 | 67 | fn send(&mut self, msg: Result) -> Result<(), Self::Error> { 68 | self.to_pc.enqueue(msg).map_err(drop) 69 | } 70 | 71 | fn receive(&mut self) -> Option { 72 | self.to_mcu.dequeue() 73 | } 74 | } 75 | 76 | /// Serial Interface half of the CommsLink type. 77 | pub struct InterfaceComms { 78 | pub to_pc: Consumer<'static, Result, N>, 79 | pub to_mcu: Producer<'static, ToMcu, N>, 80 | } 81 | } 82 | 83 | /// A trait for managing messages to or from a Worker 84 | pub trait WorkerIo { 85 | type Error; 86 | 87 | /// Send a message FROM the worker, TO the PC. 88 | fn send(&mut self, msg: Result) -> Result<(), Self::Error>; 89 | 90 | /// Receive a message FROM the PC, TO the worker 91 | fn receive(&mut self) -> Option; 92 | } 93 | 94 | /// A Pretty HAL Machine Worker 95 | /// 96 | /// This struct is intended to contain all of the shared logic between workers. 97 | /// It is highly generic, which should allow the logic to execute regardless of 98 | /// the MCU the worker is executing on. 99 | pub struct Worker 100 | where 101 | IO: WorkerIo, 102 | I2C: i2c::Write + i2c::Read + i2c::WriteRead, 103 | SPI: spi::Write + spi::Transfer, 104 | UART: serial::Write + serial::Read, 105 | { 106 | pub io: IO, 107 | pub i2c: I2C, 108 | pub spi: SPI, 109 | pub uart: UART, 110 | uart_rx: heapless::Deque, 111 | } 112 | 113 | impl Worker 114 | where 115 | IO: WorkerIo, 116 | I2C: i2c::Write + i2c::Read + i2c::WriteRead, 117 | SPI: spi::Write + spi::Transfer, 118 | UART: serial::Write + serial::Read, 119 | { 120 | pub fn new(io: IO, i2c: I2C, spi: SPI, uart: UART) -> Self { 121 | Worker { 122 | io, 123 | i2c, 124 | spi, 125 | uart, 126 | uart_rx: heapless::Deque::new(), 127 | } 128 | } 129 | /// Process any pending messages to the worker 130 | pub fn step(&mut self) -> Result<(), Error> { 131 | while let Ok(data_read) = serial::Read::::read(&mut self.uart) { 132 | self.uart_rx.push_back(data_read).ok(); 133 | } 134 | while let Some(data) = self.io.receive() { 135 | let resp = match data { 136 | ToMcu::I2c(i2c) => self.process_i2c(i2c), 137 | ToMcu::Spi(spi) => self.process_spi(spi), 138 | ToMcu::Uart(uart) => self.process_uart(uart), 139 | ToMcu::Ping => { 140 | defmt::info!("Received Ping! Responding..."); 141 | Ok(ToPc::Pong) 142 | } 143 | }; 144 | self.io.send(resp.map_err(drop)).map_err(|_| Error::Io)?; 145 | } 146 | Ok(()) 147 | } 148 | 149 | fn process_i2c(&mut self, i2c_cmd: ToMcuI2c) -> Result { 150 | match i2c_cmd { 151 | ToMcuI2c::Write { addr, output } => { 152 | // embedded_hal::blocking::i2c::Write 153 | match i2c::Write::write(&mut self.i2c, addr, &output) { 154 | Ok(_) => Ok(ToPc::I2c(ToPcI2c::WriteComplete { addr })), 155 | Err(_) => Err(Error::I2c), 156 | } 157 | } 158 | ToMcuI2c::Read { addr, to_read } => { 159 | let mut buf = [0u8; 64]; 160 | let to_read_usize = to_read as usize; 161 | 162 | if to_read_usize > buf.len() { 163 | return Err(Error::I2c); 164 | } 165 | let buf_slice = &mut buf[..to_read_usize]; 166 | 167 | match i2c::Read::read(&mut self.i2c, addr, buf_slice) { 168 | Ok(_) => Ok(ToPc::I2c(ToPcI2c::Read { 169 | addr, 170 | data_read: buf_slice.iter().cloned().collect(), 171 | })), 172 | Err(_) => Err(Error::I2c), 173 | } 174 | } 175 | ToMcuI2c::WriteThenRead { 176 | addr, 177 | output, 178 | to_read, 179 | } => { 180 | let mut buf = [0u8; 64]; 181 | let to_read_usize = to_read as usize; 182 | 183 | if to_read_usize > buf.len() { 184 | return Err(Error::I2c); 185 | } 186 | let buf_slice = &mut buf[..to_read_usize]; 187 | 188 | match i2c::WriteRead::write_read(&mut self.i2c, addr, &output, buf_slice) { 189 | Ok(_) => Ok(ToPc::I2c(ToPcI2c::WriteThenRead { 190 | addr, 191 | data_read: buf_slice.iter().cloned().collect(), 192 | })), 193 | Err(_) => Err(Error::I2c), 194 | } 195 | } 196 | } 197 | } 198 | 199 | fn process_spi(&mut self, spi_cmd: ToMcuSpi) -> Result { 200 | match spi_cmd { 201 | ToMcuSpi::Write { output } => match spi::Write::write(&mut self.spi, &output) { 202 | Ok(_) => Ok(ToPc::Spi(ToPcSpi::WriteComplete)), 203 | Err(_) => Err(Error::Spi), 204 | }, 205 | 206 | ToMcuSpi::Transfer { output } => { 207 | let mut buf = [0u8; 64]; 208 | 209 | if output.len() > buf.len() { 210 | return Err(Error::Spi); 211 | } 212 | let buf_slice = &mut buf[..output.len()]; 213 | buf_slice.copy_from_slice(&output); 214 | 215 | match spi::Transfer::transfer(&mut self.spi, buf_slice) { 216 | Ok(_) => Ok(ToPc::Spi(ToPcSpi::Transfer { 217 | data_read: buf_slice.iter().cloned().collect(), 218 | })), 219 | Err(_) => Err(Error::Spi), 220 | } 221 | } 222 | } 223 | } 224 | 225 | fn process_uart(&mut self, uart_cmd: ToMcuUart) -> Result { 226 | match uart_cmd { 227 | ToMcuUart::Write { output } => { 228 | for &b in output.iter() { 229 | nb::block!(serial::Write::::write(&mut self.uart, b)) 230 | .map_err(|_| Error::Uart)?; 231 | } 232 | Ok(ToPc::Uart(ToPcUart::WriteComplete)) 233 | } 234 | ToMcuUart::Flush => { 235 | nb::block!(serial::Write::::flush(&mut self.uart)).map_err(|_| Error::Uart)?; 236 | Ok(ToPc::Uart(ToPcUart::WriteComplete)) 237 | } 238 | ToMcuUart::Read => { 239 | let response = ToPc::Uart(ToPcUart::Read { 240 | data_read: self.uart_rx.clone().into_iter().collect(), 241 | }); 242 | self.uart_rx.clear(); 243 | Ok(response) 244 | } 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Choose a default "cargo run" tool. 2 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 3 | runner = "probe-run --chip RP2040" 4 | 5 | rustflags = [ 6 | "-C", "linker=flip-link", 7 | "-C", "link-arg=--nmagic", 8 | "-C", "link-arg=-Tlink.x", 9 | "-C", "link-arg=-Tdefmt.x", 10 | 11 | # Code-size optimizations. 12 | # trap unreachable can save a lot of space, but requires nightly compiler. 13 | # uncomment the next line if you wish to enable it 14 | # "-Z", "trap-unreachable=no", 15 | "-C", "inline-threshold=5", 16 | "-C", "no-vectorize-loops", 17 | ] 18 | 19 | [build] 20 | target = "thumbv6m-none-eabi" 21 | 22 | [env] 23 | DEFMT_LOG = "debug" 24 | 25 | [alias] 26 | rb = "run --bin" 27 | rrb = "run --release --bin" -------------------------------------------------------------------------------- /firmware/rp2040-phm/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // override the default setting (`cargo check --all-targets`) which produces the following error 3 | // "can't find crate for `test`" when the default compilation target is a no_std target 4 | // with these changes RA will call `cargo check --bins` on save 5 | "rust-analyzer.checkOnSave.allTargets": false, 6 | "rust-analyzer.checkOnSave.extraArgs": [ 7 | "--bins" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["James Munns ", "Henrik Alsér "] 3 | name = "rp2040-phm" 4 | edition = "2018" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | harness = false 9 | 10 | # needed for each integration test 11 | [[test]] 12 | name = "integration" 13 | harness = false 14 | 15 | [dependencies] 16 | cortex-m = "0.7.3" 17 | cortex-m-rt = "0.7.0" 18 | cortex-m-rtic = "1.0.0" 19 | rp2040-monotonic = "1.0.0" 20 | defmt = "0.3.0" 21 | defmt-rtt = "0.3.0" 22 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } 23 | rp-pico = "0.2.0" 24 | usb-device = "0.2.8" 25 | usbd-serial = "0.1.1" 26 | postcard = "0.7.2" 27 | embedded-hal = "0.2.6" 28 | embedded-time = "0.12.0" 29 | nb = "1.0.0" 30 | 31 | [dependencies.heapless] 32 | version = "0.7.10" 33 | features = ["serde"] 34 | 35 | [dependencies.phm-icd] 36 | path = "../../common/phm-icd" 37 | features = ["use-defmt"] 38 | 39 | [dependencies.phm-worker] 40 | path = "../phm-worker" 41 | 42 | [dev-dependencies] 43 | defmt-test = "0.3.0" 44 | 45 | # cargo build/run 46 | [profile.dev] 47 | codegen-units = 1 48 | debug = 2 49 | debug-assertions = true # <- 50 | incremental = false 51 | opt-level = 3 # <- 52 | overflow-checks = true # <- 53 | 54 | # cargo test 55 | [profile.test] 56 | codegen-units = 1 57 | debug = 2 58 | debug-assertions = true # <- 59 | incremental = false 60 | opt-level = 3 # <- 61 | overflow-checks = true # <- 62 | 63 | # cargo build/run --release 64 | [profile.release] 65 | codegen-units = 1 66 | debug = 2 67 | debug-assertions = false # <- 68 | incremental = false 69 | # NOTE disabled to work around issue rust-lang/rust#90357 70 | # the bug results in log messages not having location information 71 | # (the line printed below the log message that contains the file-line location) 72 | # lto = 'fat' 73 | opt-level = 3 # <- 74 | overflow-checks = false # <- 75 | 76 | # cargo test --release 77 | [profile.bench] 78 | codegen-units = 1 79 | debug = 2 80 | debug-assertions = false # <- 81 | incremental = false 82 | # see comment in the profile.release section 83 | lto = 'false' 84 | opt-level = 3 # <- 85 | overflow-checks = false # <- 86 | 87 | # uncomment this to switch from the crates.io version of defmt to its git version 88 | # check app-template's README for instructions 89 | # [patch.crates-io] 90 | # defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 91 | # defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 92 | # defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 93 | # panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 94 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/README.md: -------------------------------------------------------------------------------- 1 | # `app-template` 2 | 3 | > Quickly set up a [`probe-run`] + [`defmt`] + [`flip-link`] embedded project 4 | 5 | [`probe-run`]: https://crates.io/crates/probe-run 6 | [`defmt`]: https://github.com/knurling-rs/defmt 7 | [`flip-link`]: https://github.com/knurling-rs/flip-link 8 | 9 | ## Dependencies 10 | 11 | #### 1. `flip-link`: 12 | 13 | ```console 14 | $ cargo install flip-link 15 | ``` 16 | 17 | #### 2. `probe-run`: 18 | 19 | ``` console 20 | $ # make sure to install v0.2.0 or later 21 | $ cargo install probe-run 22 | ``` 23 | 24 | #### 3. [`cargo-generate`]: 25 | 26 | ``` console 27 | $ cargo install cargo-generate 28 | ``` 29 | 30 | [`cargo-generate`]: https://crates.io/crates/cargo-generate 31 | 32 | > *Note:* You can also just clone this repository instead of using `cargo-generate`, but this involves additional manual adjustments. 33 | 34 | ## Setup 35 | 36 | #### 1. Initialize the project template 37 | 38 | ``` console 39 | $ cargo generate \ 40 | --git https://github.com/knurling-rs/app-template \ 41 | --branch main \ 42 | --name my-app 43 | ``` 44 | 45 | If you look into your new `my-app` folder, you'll find that there are a few `TODO`s in the files marking the properties you need to set. 46 | 47 | Let's walk through them together now. 48 | 49 | #### 2. Set `probe-run` chip 50 | 51 | Pick a chip from `probe-run --list-chips` and enter it into `.cargo/config.toml`. 52 | 53 | If, for example, you have a nRF52840 Development Kit from one of [our workshops], replace `{{chip}}` with `nRF52840_xxAA`. 54 | 55 | [our workshops]: https://github.com/ferrous-systems/embedded-trainings-2020 56 | 57 | ``` diff 58 | # .cargo/config.toml 59 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 60 | -runner = "probe-run --chip {{chip}}" 61 | +runner = "probe-run --chip nRF52840_xxAA" 62 | ``` 63 | 64 | #### 3. Adjust the compilation target 65 | 66 | In `.cargo/config.toml`, pick the right compilation target for your board. 67 | 68 | ``` diff 69 | # .cargo/config.toml 70 | [build] 71 | -target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 72 | -# target = "thumbv7m-none-eabi" # Cortex-M3 73 | -# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) 74 | -# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 75 | +target = "thumbv7em-none-eabihf" # Cortex-M4F (with FPU) 76 | ``` 77 | 78 | Add the target with `rustup`. 79 | 80 | ``` console 81 | $ rustup target add thumbv7em-none-eabihf 82 | ``` 83 | 84 | #### 4. Add a HAL as a dependency 85 | 86 | In `Cargo.toml`, list the Hardware Abstraction Layer (HAL) for your board as a dependency. 87 | 88 | For the nRF52840 you'll want to use the [`nrf52840-hal`]. 89 | 90 | [`nrf52840-hal`]: https://crates.io/crates/nrf52840-hal 91 | 92 | ``` diff 93 | # Cargo.toml 94 | [dependencies] 95 | -# some-hal = "1.2.3" 96 | +nrf52840-hal = "0.14.0" 97 | ``` 98 | 99 | #### 5. Import your HAL 100 | 101 | Now that you have selected a HAL, fix the HAL import in `src/lib.rs` 102 | 103 | ``` diff 104 | // my-app/src/lib.rs 105 | -// use some_hal as _; // memory layout 106 | +use nrf52840_hal as _; // memory layout 107 | ``` 108 | 109 | #### (6. Get a linker script) 110 | 111 | Some HAL crates require that you manually copy over a file called `memory.x` from the HAL to the root of your project. For nrf52840-hal, this is done automatically so no action is needed. For other HAL crates, you can get it from your local Cargo folder, the default location is under: 112 | 113 | ``` 114 | ~/.cargo/registry/src/ 115 | ``` 116 | 117 | Not all HALs provide a `memory.x` file, you may need to write it yourself. Check the documentation for the HAL you are using. 118 | 119 | 120 | #### 7. Run! 121 | 122 | You are now all set to `cargo-run` your first `defmt`-powered application! 123 | There are some examples in the `src/bin` directory. 124 | 125 | Start by `cargo run`-ning `my-app/src/bin/hello.rs`: 126 | 127 | ``` console 128 | $ # `rb` is an alias for `run --bin` 129 | $ cargo rb hello 130 | Finished dev [optimized + debuginfo] target(s) in 0.03s 131 | flashing program .. 132 | DONE 133 | resetting device 134 | 0.000000 INFO Hello, world! 135 | (..) 136 | 137 | $ echo $? 138 | 0 139 | ``` 140 | 141 | If you're running out of memory (`flip-link` bails with an overflow error), you can decrease the size of the device memory buffer by setting the `DEFMT_RTT_BUFFER_SIZE` environment variable. The default value is 1024 bytes, and powers of two should be used for optimal performance: 142 | 143 | ``` console 144 | $ DEFMT_RTT_BUFFER_SIZE=64 cargo rb hello 145 | ``` 146 | 147 | #### (8. Set `rust-analyzer.linkedProjects`) 148 | 149 | If you are using [rust-analyzer] with VS Code for IDE-like features you can add following configuration to your `.vscode/settings.json` to make it work transparently across workspaces. Find the details of this option in the [RA docs]. 150 | 151 | ```json 152 | { 153 | "rust-analyzer.linkedProjects": [ 154 | "Cargo.toml", 155 | "firmware/Cargo.toml", 156 | ] 157 | } 158 | ``` 159 | 160 | [RA docs]: https://rust-analyzer.github.io/manual.html#configuration 161 | [rust-analyzer]: https://rust-analyzer.github.io/ 162 | 163 | ## Running tests 164 | 165 | The template comes configured for running unit tests and integration tests on the target. 166 | 167 | Unit tests reside in the library crate and can test private API; the initial set of unit tests are in `src/lib.rs`. 168 | `cargo test --lib` will run those unit tests. 169 | 170 | ``` console 171 | $ cargo test --lib 172 | (1/1) running `it_works`... 173 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:33 174 | all tests passed! 175 | └─ app::unit_tests::__defmt_test_entry @ src/lib.rs:28 176 | ``` 177 | 178 | Integration tests reside in the `tests` directory; the initial set of integration tests are in `tests/integration.rs`. 179 | `cargo test --test integration` will run those integration tests. 180 | Note that the argument of the `--test` flag must match the name of the test file in the `tests` directory. 181 | 182 | ``` console 183 | $ cargo test --test integration 184 | (1/1) running `it_works`... 185 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:13 186 | all tests passed! 187 | └─ integration::tests::__defmt_test_entry @ tests/integration.rs:8 188 | ``` 189 | 190 | Note that to add a new test file to the `tests` directory you also need to add a new `[[test]]` section to `Cargo.toml`. 191 | 192 | ## Trying out the git version of defmt 193 | 194 | This template is configured to use the latest crates.io release (the "stable" release) of the `defmt` framework. 195 | To use the git version (the "development" version) of `defmt` follow these steps: 196 | 197 | 1. Install the *git* version of `probe-run` 198 | 199 | ``` console 200 | $ cargo install --git https://github.com/knurling-rs/probe-run --branch main 201 | ``` 202 | 203 | 2. Check which defmt version `probe-run` supports 204 | 205 | ``` console 206 | $ probe-run --version 207 | 0.2.0 (aa585f2 2021-02-22) 208 | supported defmt version: 60c6447f8ecbc4ff023378ba6905bcd0de1e679f 209 | ``` 210 | 211 | In the example output, the supported version is `60c6447f8ecbc4ff023378ba6905bcd0de1e679f` 212 | 213 | 3. Switch defmt dependencies to git: uncomment the last part of the root `Cargo.toml` and enter the hash reported by `probe-run --version`: 214 | 215 | ``` diff 216 | -# [patch.crates-io] 217 | -# defmt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 218 | -# defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 219 | -# defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 220 | -# panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "use defmt version reported by `probe-run --version`" } 221 | +[patch.crates-io] 222 | +defmt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 223 | +defmt-rtt = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 224 | +defmt-test = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 225 | +panic-probe = { git = "https://github.com/knurling-rs/defmt", rev = "60c6447f8ecbc4ff023378ba6905bcd0de1e679f" } 226 | ``` 227 | 228 | You are now using the git version of `defmt`! 229 | 230 | **NOTE** there may have been breaking changes between the crates.io version and the git version; you'll need to fix those in the source code. 231 | 232 | ## Support 233 | 234 | `app-template` is part of the [Knurling] project, [Ferrous Systems]' effort at 235 | improving tooling used to develop for embedded systems. 236 | 237 | If you think that our work is useful, consider sponsoring it via [GitHub 238 | Sponsors]. 239 | 240 | ## License 241 | 242 | Licensed under either of 243 | 244 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 245 | http://www.apache.org/licenses/LICENSE-2.0) 246 | 247 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 248 | 249 | at your option. 250 | 251 | ### Contribution 252 | 253 | Unless you explicitly state otherwise, any contribution intentionally submitted 254 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 255 | licensed as above, without any additional terms or conditions. 256 | 257 | [Knurling]: https://knurling.ferrous-systems.com 258 | [Ferrous Systems]: https://ferrous-systems.com/ 259 | [GitHub Sponsors]: https://github.com/sponsors/knurling-rs 260 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/build.rs: -------------------------------------------------------------------------------- 1 | //! This build script copies the `memory.x` file from the crate root into 2 | //! a directory where the linker can always find it at build time. 3 | //! For many projects this is optional, as the linker always searches the 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you 5 | //! are using a workspace or have a more complicated build setup, this 6 | //! build script becomes required. Additionally, by requesting that 7 | //! Cargo re-run the build script whenever `memory.x` is changed, 8 | //! updating `memory.x` ensures a rebuild of the application with the 9 | //! new memory settings. 10 | 11 | use std::env; 12 | use std::fs::File; 13 | use std::io::Write; 14 | use std::path::PathBuf; 15 | 16 | fn main() { 17 | // Put `memory.x` in our output directory and ensure it's 18 | // on the linker search path. 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 20 | File::create(out.join("memory.x")) 21 | .unwrap() 22 | .write_all(include_bytes!("memory.x")) 23 | .unwrap(); 24 | println!("cargo:rustc-link-search={}", out.display()); 25 | 26 | // By default, Cargo will re-run a build script whenever 27 | // any file in the project changes. By specifying `memory.x` 28 | // here, we ensure the build script is only re-run when 29 | // `memory.x` is changed. 30 | println!("cargo:rerun-if-changed=memory.x"); 31 | } 32 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 3 | FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 4 | RAM : ORIGIN = 0x20000000, LENGTH = 256K 5 | } 6 | 7 | EXTERN(BOOT2_FIRMWARE) 8 | 9 | SECTIONS { 10 | /* ### Boot loader */ 11 | .boot2 ORIGIN(BOOT2) : 12 | { 13 | KEEP(*(.boot2)); 14 | } > BOOT2 15 | } INSERT BEFORE .text; -------------------------------------------------------------------------------- /firmware/rp2040-phm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::sync::atomic::{AtomicUsize, Ordering}; 4 | use defmt_rtt as _; 5 | use panic_probe as _; 6 | 7 | defmt::timestamp! {"{=u64}", { 8 | static COUNT: AtomicUsize = AtomicUsize::new(0); 9 | // NOTE(no-CAS) `timestamps` runs with interrupts disabled 10 | let n = COUNT.load(Ordering::Relaxed); 11 | COUNT.store(n + 1, Ordering::Relaxed); 12 | n as u64 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use rp2040_phm as _; // global logger + panicking-behavior + memory layout 5 | 6 | #[rtic::app(device = rp_pico::hal::pac, dispatchers = [XIP_IRQ])] 7 | mod app { 8 | use defmt::unwrap; 9 | use embedded_time::rate::Extensions; 10 | use heapless::spsc::Queue; 11 | use phm_icd::{ToMcu, ToPc}; 12 | use phm_worker::{ 13 | comms::{CommsLink, InterfaceComms, WorkerComms}, 14 | Worker, 15 | }; 16 | use postcard::{to_vec_cobs, CobsAccumulator, FeedResult}; 17 | use rp2040_monotonic::*; 18 | use rp_pico::{ 19 | hal::{ 20 | clocks::init_clocks_and_plls, 21 | gpio::pin::{ 22 | bank0::{Gpio16, Gpio17}, 23 | FunctionI2C, FunctionSpi, FunctionUart, Pin, 24 | }, 25 | spi::{self, Spi}, 26 | uart::{common_configs as UartConfig, Enabled as UartEnabled, UartPeripheral}, 27 | usb::UsbBus, 28 | watchdog::Watchdog, 29 | Clock, Sio, I2C, 30 | }, 31 | pac::{I2C0, SPI0, UART0}, 32 | XOSC_CRYSTAL_FREQ, 33 | }; 34 | use usb_device::{class_prelude::*, prelude::*}; 35 | use usbd_serial::{SerialPort, USB_CLASS_CDC}; 36 | type PhmI2c = I2C, Pin)>; 37 | type PhmSpi = Spi; 38 | type PhmUart = UartPeripheral; 39 | 40 | #[monotonic(binds = TIMER_IRQ_0, default = true)] 41 | type Monotonic = Rp2040Monotonic; 42 | 43 | #[shared] 44 | struct Shared {} 45 | 46 | #[local] 47 | struct Local { 48 | interface_comms: InterfaceComms<8>, 49 | worker: Worker, PhmI2c, PhmSpi, PhmUart>, 50 | usb_serial: SerialPort<'static, UsbBus>, 51 | usb_dev: UsbDevice<'static, UsbBus>, 52 | } 53 | 54 | #[init(local = [ 55 | usb_bus: Option> = None, 56 | incoming: Queue = Queue::new(), 57 | outgoing: Queue, 8> = Queue::new(), 58 | ])] 59 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 60 | let device = cx.device; 61 | 62 | // Setup clocks 63 | let mut resets = device.RESETS; 64 | let mut watchdog = Watchdog::new(device.WATCHDOG); 65 | let clocks = init_clocks_and_plls( 66 | XOSC_CRYSTAL_FREQ, 67 | device.XOSC, 68 | device.CLOCKS, 69 | device.PLL_SYS, 70 | device.PLL_USB, 71 | &mut resets, 72 | &mut watchdog, 73 | ) 74 | .ok() 75 | .unwrap(); 76 | let pclk_freq = clocks.peripheral_clock.freq(); 77 | 78 | // Configure the monotonic timer 79 | let mono = Monotonic::new(device.TIMER); 80 | 81 | // The single-cycle I/O block controls our GPIO pins 82 | let sio = Sio::new(device.SIO); 83 | 84 | // Set the pins up according to their function on this particular board 85 | let pins = rp_pico::Pins::new( 86 | device.IO_BANK0, 87 | device.PADS_BANK0, 88 | sio.gpio_bank0, 89 | &mut resets, 90 | ); 91 | 92 | // Set up the I2C pins and driver 93 | let sda_pin = pins.gpio16.into_mode::(); 94 | let scl_pin = pins.gpio17.into_mode::(); 95 | let i2c = I2C::i2c0( 96 | device.I2C0, 97 | sda_pin, 98 | scl_pin, 99 | 100.kHz(), 100 | &mut resets, 101 | clocks.peripheral_clock, 102 | ); 103 | 104 | // Set up the SPI pins and driver 105 | let _sck = pins.gpio2.into_mode::(); 106 | let _mosi = pins.gpio3.into_mode::(); 107 | let _miso = pins.gpio4.into_mode::(); 108 | let spi = Spi::<_, _, 8>::new(device.SPI0).init( 109 | &mut resets, 110 | pclk_freq, 111 | 2_000_000_u32.Hz(), 112 | &embedded_hal::spi::MODE_0, 113 | ); 114 | 115 | // Set up UART 116 | let _tx_pin = pins.gpio0.into_mode::(); 117 | let _rx_pin = pins.gpio1.into_mode::(); 118 | let uart = UartPeripheral::new(device.UART0, &mut resets) 119 | .enable(UartConfig::_9600_8_N_1, pclk_freq) 120 | .unwrap(); 121 | 122 | // Set up USB Serial Port 123 | let usb_bus = cx.local.usb_bus; 124 | usb_bus.replace(UsbBusAllocator::new(UsbBus::new( 125 | device.USBCTRL_REGS, 126 | device.USBCTRL_DPRAM, 127 | clocks.usb_clock, 128 | true, 129 | &mut resets, 130 | ))); 131 | let usb_serial = SerialPort::new(usb_bus.as_ref().unwrap()); 132 | let usb_dev = UsbDeviceBuilder::new(usb_bus.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) 133 | .manufacturer("OVAR Labs") 134 | .product("Powerbus Mini") 135 | // TODO: Use some kind of unique ID. This will probably require another singleton, 136 | // as the storage must be static. Probably heapless::String -> singleton!() 137 | .serial_number("ajm123") 138 | .device_class(USB_CLASS_CDC) 139 | .max_packet_size_0(64) // (makes control transfers 8x faster) 140 | .build(); 141 | 142 | let comms = CommsLink { 143 | to_pc: cx.local.outgoing, 144 | to_mcu: cx.local.incoming, 145 | }; 146 | 147 | let (worker_comms, interface_comms) = comms.split(); 148 | 149 | let worker = Worker::new(worker_comms, i2c, spi, uart); 150 | 151 | usb_tick::spawn().ok(); 152 | ( 153 | Shared {}, 154 | Local { 155 | worker, 156 | interface_comms, 157 | usb_serial, 158 | usb_dev, 159 | }, 160 | init::Monotonics(mono), 161 | ) 162 | } 163 | 164 | #[task(local = [usb_serial, interface_comms, usb_dev, cobs_buf: CobsAccumulator<512> = CobsAccumulator::new()])] 165 | fn usb_tick(cx: usb_tick::Context) { 166 | let usb_serial = cx.local.usb_serial; 167 | let usb_dev = cx.local.usb_dev; 168 | let cobs_buf = cx.local.cobs_buf; 169 | let interface_comms = cx.local.interface_comms; 170 | 171 | let mut buf = [0u8; 128]; 172 | 173 | usb_dev.poll(&mut [usb_serial]); 174 | 175 | if let Some(out) = interface_comms.to_pc.dequeue() { 176 | if let Ok(ser_msg) = to_vec_cobs::<_, 128>(&out) { 177 | usb_serial.write(&ser_msg).ok(); 178 | } else { 179 | defmt::panic!("Serialization error!"); 180 | } 181 | } 182 | 183 | match usb_serial.read(&mut buf) { 184 | Ok(sz) if sz > 0 => { 185 | let buf = &buf[..sz]; 186 | let mut window = &buf[..]; 187 | 188 | 'cobs: while !window.is_empty() { 189 | window = match cobs_buf.feed::(&window) { 190 | FeedResult::Consumed => break 'cobs, 191 | FeedResult::OverFull(new_wind) => new_wind, 192 | FeedResult::DeserError(new_wind) => new_wind, 193 | FeedResult::Success { data, remaining } => { 194 | defmt::println!("got: {:?}", data); 195 | interface_comms.to_mcu.enqueue(data).ok(); 196 | remaining 197 | } 198 | }; 199 | } 200 | } 201 | Ok(_) | Err(usb_device::UsbError::WouldBlock) => {} 202 | Err(_e) => defmt::panic!("Usb Error!"), 203 | } 204 | usb_tick::spawn_after(1.millis()).ok(); 205 | } 206 | 207 | #[idle(local = [worker])] 208 | fn idle(cx: idle::Context) -> ! { 209 | defmt::println!("Hello, world!"); 210 | let worker = cx.local.worker; 211 | 212 | loop { 213 | unwrap!(worker.step().map_err(drop)); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /firmware/rp2040-phm/tests/integration.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use rp2040_phm as _; // memory layout + panic handler 5 | 6 | // See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state' 7 | // feature) 8 | #[defmt_test::tests] 9 | mod tests { 10 | use defmt::assert; 11 | 12 | #[test] 13 | fn it_works() { 14 | assert!(true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /host/phm-cli/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "CoreFoundation-sys" 7 | version = "0.1.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b" 10 | dependencies = [ 11 | "libc", 12 | "mach 0.1.2", 13 | ] 14 | 15 | [[package]] 16 | name = "IOKit-sys" 17 | version = "0.1.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a" 20 | dependencies = [ 21 | "CoreFoundation-sys", 22 | "libc", 23 | "mach 0.1.2", 24 | ] 25 | 26 | [[package]] 27 | name = "aho-corasick" 28 | version = "0.7.18" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 31 | dependencies = [ 32 | "memchr", 33 | ] 34 | 35 | [[package]] 36 | name = "atomic-polyfill" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "e686d748538a32325b28d6411dd8a939e7ad5128e5d0023cc4fd3573db456042" 40 | dependencies = [ 41 | "critical-section", 42 | "riscv-target", 43 | ] 44 | 45 | [[package]] 46 | name = "atty" 47 | version = "0.2.14" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 50 | dependencies = [ 51 | "hermit-abi", 52 | "libc", 53 | "winapi", 54 | ] 55 | 56 | [[package]] 57 | name = "autocfg" 58 | version = "1.1.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 61 | 62 | [[package]] 63 | name = "bare-metal" 64 | version = "0.2.5" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 67 | dependencies = [ 68 | "rustc_version", 69 | ] 70 | 71 | [[package]] 72 | name = "bare-metal" 73 | version = "1.0.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 76 | 77 | [[package]] 78 | name = "bit_field" 79 | version = "0.10.1" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 82 | 83 | [[package]] 84 | name = "bitfield" 85 | version = "0.13.2" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 88 | 89 | [[package]] 90 | name = "bitflags" 91 | version = "1.3.2" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 94 | 95 | [[package]] 96 | name = "byteorder" 97 | version = "1.4.3" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 100 | 101 | [[package]] 102 | name = "cc" 103 | version = "1.0.72" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 106 | 107 | [[package]] 108 | name = "cfg-if" 109 | version = "0.1.10" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 112 | 113 | [[package]] 114 | name = "cfg-if" 115 | version = "1.0.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 118 | 119 | [[package]] 120 | name = "clap" 121 | version = "3.0.14" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62" 124 | dependencies = [ 125 | "atty", 126 | "bitflags", 127 | "clap_derive", 128 | "indexmap", 129 | "lazy_static", 130 | "os_str_bytes", 131 | "strsim", 132 | "termcolor", 133 | "textwrap", 134 | ] 135 | 136 | [[package]] 137 | name = "clap_derive" 138 | version = "3.0.14" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85" 141 | dependencies = [ 142 | "heck", 143 | "proc-macro-error", 144 | "proc-macro2", 145 | "quote", 146 | "syn", 147 | ] 148 | 149 | [[package]] 150 | name = "cortex-m" 151 | version = "0.7.4" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" 154 | dependencies = [ 155 | "bare-metal 0.2.5", 156 | "bitfield", 157 | "embedded-hal", 158 | "volatile-register", 159 | ] 160 | 161 | [[package]] 162 | name = "critical-section" 163 | version = "0.2.5" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" 166 | dependencies = [ 167 | "bare-metal 1.0.0", 168 | "cfg-if 1.0.0", 169 | "cortex-m", 170 | "riscv", 171 | ] 172 | 173 | [[package]] 174 | name = "embedded-hal" 175 | version = "0.2.6" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" 178 | dependencies = [ 179 | "nb 0.1.3", 180 | "void", 181 | ] 182 | 183 | [[package]] 184 | name = "hash32" 185 | version = "0.2.1" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 188 | dependencies = [ 189 | "byteorder", 190 | ] 191 | 192 | [[package]] 193 | name = "hashbrown" 194 | version = "0.11.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 197 | 198 | [[package]] 199 | name = "heapless" 200 | version = "0.7.10" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" 203 | dependencies = [ 204 | "atomic-polyfill", 205 | "hash32", 206 | "serde", 207 | "spin", 208 | "stable_deref_trait", 209 | ] 210 | 211 | [[package]] 212 | name = "heck" 213 | version = "0.4.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 216 | 217 | [[package]] 218 | name = "hermit-abi" 219 | version = "0.1.19" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 222 | dependencies = [ 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "indexmap" 228 | version = "1.8.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" 231 | dependencies = [ 232 | "autocfg", 233 | "hashbrown", 234 | ] 235 | 236 | [[package]] 237 | name = "lazy_static" 238 | version = "1.4.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 241 | 242 | [[package]] 243 | name = "libc" 244 | version = "0.2.117" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" 247 | 248 | [[package]] 249 | name = "libudev" 250 | version = "0.2.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe" 253 | dependencies = [ 254 | "libc", 255 | "libudev-sys", 256 | ] 257 | 258 | [[package]] 259 | name = "libudev-sys" 260 | version = "0.1.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 263 | dependencies = [ 264 | "libc", 265 | "pkg-config", 266 | ] 267 | 268 | [[package]] 269 | name = "lock_api" 270 | version = "0.4.6" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 273 | dependencies = [ 274 | "scopeguard", 275 | ] 276 | 277 | [[package]] 278 | name = "mach" 279 | version = "0.1.2" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" 282 | dependencies = [ 283 | "libc", 284 | ] 285 | 286 | [[package]] 287 | name = "mach" 288 | version = "0.2.3" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" 291 | dependencies = [ 292 | "libc", 293 | ] 294 | 295 | [[package]] 296 | name = "memchr" 297 | version = "2.4.1" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 300 | 301 | [[package]] 302 | name = "nb" 303 | version = "0.1.3" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 306 | dependencies = [ 307 | "nb 1.0.0", 308 | ] 309 | 310 | [[package]] 311 | name = "nb" 312 | version = "1.0.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 315 | 316 | [[package]] 317 | name = "nix" 318 | version = "0.16.1" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb" 321 | dependencies = [ 322 | "bitflags", 323 | "cc", 324 | "cfg-if 0.1.10", 325 | "libc", 326 | "void", 327 | ] 328 | 329 | [[package]] 330 | name = "os_str_bytes" 331 | version = "6.0.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 334 | dependencies = [ 335 | "memchr", 336 | ] 337 | 338 | [[package]] 339 | name = "phm" 340 | version = "0.0.2" 341 | dependencies = [ 342 | "embedded-hal", 343 | "heapless", 344 | "nb 1.0.0", 345 | "phm-icd", 346 | "postcard", 347 | "serde", 348 | "serialport", 349 | ] 350 | 351 | [[package]] 352 | name = "phm-cli" 353 | version = "0.0.2" 354 | dependencies = [ 355 | "clap", 356 | "embedded-hal", 357 | "phm", 358 | "serialport", 359 | ] 360 | 361 | [[package]] 362 | name = "phm-icd" 363 | version = "0.0.2" 364 | dependencies = [ 365 | "heapless", 366 | "serde", 367 | ] 368 | 369 | [[package]] 370 | name = "pkg-config" 371 | version = "0.3.24" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 374 | 375 | [[package]] 376 | name = "postcard" 377 | version = "0.7.3" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" 380 | dependencies = [ 381 | "heapless", 382 | "postcard-cobs", 383 | "serde", 384 | ] 385 | 386 | [[package]] 387 | name = "postcard-cobs" 388 | version = "0.1.5-pre" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" 391 | 392 | [[package]] 393 | name = "proc-macro-error" 394 | version = "1.0.4" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 397 | dependencies = [ 398 | "proc-macro-error-attr", 399 | "proc-macro2", 400 | "quote", 401 | "syn", 402 | "version_check", 403 | ] 404 | 405 | [[package]] 406 | name = "proc-macro-error-attr" 407 | version = "1.0.4" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 410 | dependencies = [ 411 | "proc-macro2", 412 | "quote", 413 | "version_check", 414 | ] 415 | 416 | [[package]] 417 | name = "proc-macro2" 418 | version = "1.0.36" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 421 | dependencies = [ 422 | "unicode-xid", 423 | ] 424 | 425 | [[package]] 426 | name = "quote" 427 | version = "1.0.15" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 430 | dependencies = [ 431 | "proc-macro2", 432 | ] 433 | 434 | [[package]] 435 | name = "regex" 436 | version = "1.5.4" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 439 | dependencies = [ 440 | "aho-corasick", 441 | "memchr", 442 | "regex-syntax", 443 | ] 444 | 445 | [[package]] 446 | name = "regex-syntax" 447 | version = "0.6.25" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 450 | 451 | [[package]] 452 | name = "riscv" 453 | version = "0.7.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 456 | dependencies = [ 457 | "bare-metal 1.0.0", 458 | "bit_field", 459 | "riscv-target", 460 | ] 461 | 462 | [[package]] 463 | name = "riscv-target" 464 | version = "0.1.2" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 467 | dependencies = [ 468 | "lazy_static", 469 | "regex", 470 | ] 471 | 472 | [[package]] 473 | name = "rustc_version" 474 | version = "0.2.3" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 477 | dependencies = [ 478 | "semver", 479 | ] 480 | 481 | [[package]] 482 | name = "scopeguard" 483 | version = "1.1.0" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 486 | 487 | [[package]] 488 | name = "semver" 489 | version = "0.9.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 492 | dependencies = [ 493 | "semver-parser", 494 | ] 495 | 496 | [[package]] 497 | name = "semver-parser" 498 | version = "0.7.0" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 501 | 502 | [[package]] 503 | name = "serde" 504 | version = "1.0.136" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 507 | dependencies = [ 508 | "serde_derive", 509 | ] 510 | 511 | [[package]] 512 | name = "serde_derive" 513 | version = "1.0.136" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 516 | dependencies = [ 517 | "proc-macro2", 518 | "quote", 519 | "syn", 520 | ] 521 | 522 | [[package]] 523 | name = "serialport" 524 | version = "4.0.1" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "5d8cd7c0f22290ee2c01457009fa6fc1cae4153d5608a924e5dc423babc2c655" 527 | dependencies = [ 528 | "CoreFoundation-sys", 529 | "IOKit-sys", 530 | "bitflags", 531 | "cfg-if 0.1.10", 532 | "libudev", 533 | "mach 0.2.3", 534 | "nix", 535 | "regex", 536 | "winapi", 537 | ] 538 | 539 | [[package]] 540 | name = "spin" 541 | version = "0.9.2" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" 544 | dependencies = [ 545 | "lock_api", 546 | ] 547 | 548 | [[package]] 549 | name = "stable_deref_trait" 550 | version = "1.2.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 553 | 554 | [[package]] 555 | name = "strsim" 556 | version = "0.10.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 559 | 560 | [[package]] 561 | name = "syn" 562 | version = "1.0.86" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 565 | dependencies = [ 566 | "proc-macro2", 567 | "quote", 568 | "unicode-xid", 569 | ] 570 | 571 | [[package]] 572 | name = "termcolor" 573 | version = "1.1.2" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 576 | dependencies = [ 577 | "winapi-util", 578 | ] 579 | 580 | [[package]] 581 | name = "textwrap" 582 | version = "0.14.2" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" 585 | 586 | [[package]] 587 | name = "unicode-xid" 588 | version = "0.2.2" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 591 | 592 | [[package]] 593 | name = "vcell" 594 | version = "0.1.3" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 597 | 598 | [[package]] 599 | name = "version_check" 600 | version = "0.9.4" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 603 | 604 | [[package]] 605 | name = "void" 606 | version = "1.0.2" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 609 | 610 | [[package]] 611 | name = "volatile-register" 612 | version = "0.2.1" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" 615 | dependencies = [ 616 | "vcell", 617 | ] 618 | 619 | [[package]] 620 | name = "winapi" 621 | version = "0.3.9" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 624 | dependencies = [ 625 | "winapi-i686-pc-windows-gnu", 626 | "winapi-x86_64-pc-windows-gnu", 627 | ] 628 | 629 | [[package]] 630 | name = "winapi-i686-pc-windows-gnu" 631 | version = "0.4.0" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 634 | 635 | [[package]] 636 | name = "winapi-util" 637 | version = "0.1.5" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 640 | dependencies = [ 641 | "winapi", 642 | ] 643 | 644 | [[package]] 645 | name = "winapi-x86_64-pc-windows-gnu" 646 | version = "0.4.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 649 | -------------------------------------------------------------------------------- /host/phm-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phm-cli" 3 | version = "0.0.2" 4 | description = "The Interface Control Document (ICD) for Pretty HAL Machine" 5 | repository = "https://github.com/jamesmunns/pretty-hal-machine" 6 | authors = [ 7 | "James Munns ", 8 | "Henrik Alsér ", 9 | "Tirth Jain ", 10 | ] 11 | edition = "2021" 12 | readme = "README.md" 13 | 14 | categories = [ 15 | "embedded", 16 | ] 17 | license = "MIT OR Apache-2.0" 18 | 19 | [dependencies] 20 | embedded-hal = "0.2.6" 21 | serialport = "4.0.1" 22 | clap = { version = "3.0.14", features = ["derive"] } 23 | 24 | [dependencies.phm] 25 | path = "../phm" 26 | version = "0.0.2" 27 | -------------------------------------------------------------------------------- /host/phm-cli/README.md: -------------------------------------------------------------------------------- 1 | # pretty hal machine cli 2 | 3 | ## Top Level (`phm-cli`) 4 | 5 | ``` 6 | phm-cli 7 | 8 | USAGE: 9 | phm-cli 10 | 11 | OPTIONS: 12 | -h, --help Print help information 13 | 14 | SUBCOMMANDS: 15 | help Print this message or the help of the given subcommand(s) 16 | i2c Commands for I2C communication 17 | ``` 18 | 19 | ## I2C Commands (`phm-cli i2c`) 20 | 21 | ``` 22 | phm-cli-i2c 23 | Commands for I2C communication 24 | 25 | USAGE: 26 | phm-cli i2c 27 | 28 | OPTIONS: 29 | -h, --help Print help information 30 | 31 | SUBCOMMANDS: 32 | help Print this message or the help of the given subcommand(s) 33 | console I2C Write console mode 34 | read Read count bytes from the given address 35 | write Write bytes to the given address 36 | write-read Write-Read bytes to and from the given address 37 | ``` 38 | 39 | ### I2C Write console mode (`phm-cli i2c console`) 40 | 41 | ``` 42 | phm-cli-i2c-console 43 | Write bytes over I2C. 44 | Provide a comma separated list of bytes (hex) then press enter to execute. 45 | 46 | USAGE: 47 | phm-cli i2c console -a
48 | 49 | OPTIONS: 50 | -a
The address to write to 51 | -h, --help Print help information 52 | ``` 53 | 54 | ### I2C Read (`phm-cli i2c read`) 55 | 56 | ``` 57 | phm-cli-i2c-read 58 | Read count bytes from the given address 59 | 60 | USAGE: 61 | phm-cli i2c read -a
--read-ct 62 | 63 | OPTIONS: 64 | -a
The address to write to 65 | -h, --help Print help information 66 | --read-ct Number of bytes to read 67 | ``` 68 | 69 | ### I2C Write (`phm-cli i2c write`) 70 | 71 | ``` 72 | phm-cli-i2c-write 73 | Write bytes to the given address 74 | 75 | USAGE: 76 | phm-cli i2c write -a
--write 77 | 78 | OPTIONS: 79 | -a
The address to write to 80 | -b, --write Bytes to write to the address. Should be given as a comma-separated 81 | list of hex values. For example: "0xA0,0xAB,0x11" 82 | -h, --help Print help information 83 | ``` 84 | 85 | ### I2C Write then Read (`phm-cli i2c write-read`) 86 | 87 | ``` 88 | phm-cli-i2c-write-read 89 | Write-Read bytes to and from the given address 90 | 91 | USAGE: 92 | phm-cli i2c write-read -a
--bytes --read-ct 93 | 94 | OPTIONS: 95 | -a
The address to write to. Should be given as a hex value. For 96 | example: "0xA4" 97 | -b, --bytes 98 | -h, --help Print help information 99 | --read-ct Bytes to write to the address. Should be given as a comma- 100 | separated list of hex values. For example: "0xA0,0xAB,0x11" 101 | ``` 102 | 103 | ## SPI Commands (`phm-cli spi`) 104 | 105 | ``` 106 | phm-cli-spi 107 | Commands for SPI communication 108 | 109 | USAGE: 110 | phm-cli spi 111 | 112 | OPTIONS: 113 | -h, --help Print help information 114 | 115 | SUBCOMMANDS: 116 | help Print this message or the help of the given subcommand(s) 117 | console SPI Transfer console mode 118 | transfer Write and read bytes over SPI 119 | write Write bytes over SPI 120 | ``` 121 | 122 | ### SPI Transfer console mode (`phm-cli spi console`) 123 | 124 | ``` 125 | phm-cli-spi-console 126 | Write and read bytes over SPI. 127 | Provide a comma separated list of bytes (hex) then press enter to execute. 128 | 129 | USAGE: 130 | phm-cli spi console 131 | 132 | OPTIONS: 133 | -h, --help Print help information 134 | ``` 135 | 136 | ### SPI Transfer (`phm-cli spi transfer`) 137 | 138 | ``` 139 | phm-cli-spi-transfer 140 | Write and read bytes over SPI 141 | 142 | USAGE: 143 | phm-cli spi transfer --write 144 | 145 | OPTIONS: 146 | -b, --write Bytes to write to SPI. Should be given as a comma-separated 147 | list of hex values. For example: "0xA0,0xAB,0x11" 148 | -h, --help Print help information 149 | ``` 150 | 151 | ### SPI Write (`phm-cli spi write`) 152 | 153 | ``` 154 | phm-cli-spi-write 155 | Write bytes over SPI 156 | 157 | USAGE: 158 | phm-cli spi write --write 159 | 160 | OPTIONS: 161 | -b, --write Bytes to write to SPI. Should be given as a comma-separated 162 | list of hex values. For example: "0xA0,0xAB,0x11" 163 | -h, --help Print help information 164 | ``` 165 | 166 | ### UART RX console mode (`phm-cli uart listen`) 167 | 168 | ``` 169 | phm-cli-uart-listen 170 | Read incoming bytes over UART. 171 | 172 | USAGE: 173 | phm-cli uart listen 174 | 175 | OPTIONS: 176 | -h, --help Print help information 177 | ``` 178 | 179 | ### UART TX console mode (`phm-cli uart console`) 180 | 181 | ``` 182 | phm-cli-uart-console 183 | Write bytes over UART. 184 | Provide a comma separated list of bytes (hex) then press enter to execute. 185 | 186 | USAGE: 187 | phm-cli uart console 188 | 189 | OPTIONS: 190 | -h, --help Print help information 191 | ``` 192 | 193 | ### UART Write (`phm-cli uart write`) 194 | 195 | ``` 196 | phm-cli-uart-write 197 | Write bytes over UART 198 | 199 | USAGE: 200 | phm-cli uart write --write 201 | 202 | OPTIONS: 203 | -b, --write Bytes to write to UART. Should be given as a comma-separated 204 | list of hex values. For example: "0xA0,0xAB,0x11" 205 | -h, --help Print help information 206 | ``` 207 | -------------------------------------------------------------------------------- /host/phm-cli/src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::{num::ParseIntError, str::FromStr}; 2 | 3 | use clap::{Args, Parser, Subcommand}; 4 | use phm::Machine; 5 | 6 | #[derive(Debug)] 7 | struct Address(u8); 8 | 9 | #[derive(Debug)] 10 | struct WriteBytes(Vec); 11 | 12 | #[derive(Parser, Debug)] 13 | pub enum PhmCli { 14 | /// Commands for I2C communication. 15 | I2C(I2C), 16 | /// Commands for SPI communication. 17 | Spi(Spi), 18 | /// Commands for SPI communication. 19 | Uart(Uart), 20 | } 21 | 22 | #[derive(Parser, Debug)] 23 | pub struct I2C { 24 | #[clap(subcommand)] 25 | command: I2CCommand, 26 | } 27 | 28 | #[derive(Parser, Debug)] 29 | pub struct Spi { 30 | #[clap(subcommand)] 31 | command: SpiCommand, 32 | } 33 | 34 | #[derive(Parser, Debug)] 35 | pub struct Uart { 36 | #[clap(subcommand)] 37 | command: UartCommand, 38 | } 39 | 40 | #[derive(Subcommand, Debug)] 41 | enum I2CCommand { 42 | /// Write bytes to the given address 43 | #[clap(name = "write")] 44 | I2CWrite(I2CWrite), 45 | /// Read count bytes from the given address 46 | #[clap(name = "read")] 47 | I2CRead(I2CRead), 48 | /// Write-Read bytes to and from the given address 49 | #[clap(name = "write-read")] 50 | WriteRead(WriteRead), 51 | /// I2C Write console mode 52 | #[clap(name = "console")] 53 | I2CConsole(I2CConsole), 54 | } 55 | 56 | #[derive(Subcommand, Debug)] 57 | enum SpiCommand { 58 | /// Write bytes over SPI 59 | #[clap(name = "write")] 60 | SpiWrite(SpiWrite), 61 | /// Transfer bytes over SPI 62 | #[clap(name = "transfer")] 63 | SpiTransfer(SpiTransfer), 64 | /// SPI Transfer console mode 65 | #[clap(name = "console")] 66 | SpiConsole, 67 | } 68 | 69 | #[derive(Subcommand, Debug)] 70 | enum UartCommand { 71 | /// Write bytes over UART 72 | #[clap(name = "write")] 73 | UartWrite(UartWrite), 74 | /// UART Write console mode 75 | #[clap(name = "console")] 76 | UartConsole, 77 | /// UART Read console 78 | #[clap(name = "listen")] 79 | UartListen, 80 | } 81 | 82 | #[derive(Args, Debug)] 83 | struct I2CWrite { 84 | /// The address to write to. 85 | #[clap(short = 'a')] 86 | address: Address, 87 | /// Bytes to write to the address. Should be given as a comma-separated list of hex values. For example: "0xA0,0xAB,0x11". 88 | #[clap(short = 'b', long = "write")] 89 | write_bytes: WriteBytes, 90 | } 91 | 92 | #[derive(Args, Debug)] 93 | struct I2CRead { 94 | /// The address to write to. 95 | #[clap(short = 'a')] 96 | address: Address, 97 | /// Number of bytes to read. 98 | #[clap(long = "read-ct")] 99 | read_count: usize, 100 | } 101 | 102 | #[derive(Args, Debug)] 103 | struct WriteRead { 104 | /// The address to write to. Should be given as a hex value. For example: "0xA4". 105 | #[clap(short = 'a')] 106 | address: Address, 107 | #[clap(short = 'b', long = "bytes")] 108 | write_bytes: WriteBytes, 109 | /// Bytes to write to the address. Should be given as a comma-separated list of hex values. For example: "0xA0,0xAB,0x11". 110 | #[clap(long = "read-ct")] 111 | read_count: usize, 112 | } 113 | 114 | #[derive(Args, Debug)] 115 | struct I2CConsole { 116 | /// The address to write to. Should be given as a hex value. For example: "0xA4". 117 | #[clap(short = 'a')] 118 | address: Address, 119 | } 120 | 121 | #[derive(Args, Debug)] 122 | struct SpiWrite { 123 | /// Bytes to write over SPI. Should be given as a comma-separated list of hex values. For example: "0xA0,0xAB,0x11". 124 | #[clap(short = 'b', long = "write")] 125 | write_bytes: WriteBytes, 126 | } 127 | 128 | #[derive(Args, Debug)] 129 | struct SpiTransfer { 130 | /// Bytes to transfer over SPI. Should be given as a comma-separated list of hex values. For example: "0xA0,0xAB,0x11". 131 | #[clap(short = 'b', long = "write")] 132 | write_bytes: WriteBytes, 133 | } 134 | 135 | #[derive(Args, Debug)] 136 | struct UartWrite { 137 | /// Bytes to write over UART. Should be given as a comma-separated list of hex values. For example: "0xA0,0xAB,0x11". 138 | #[clap(short = 'b', long = "write")] 139 | write_bytes: WriteBytes, 140 | } 141 | 142 | impl PhmCli { 143 | pub fn run(&self, machine: &mut Machine) -> Result { 144 | match self { 145 | PhmCli::I2C(cmd) => match &cmd.command { 146 | I2CCommand::I2CWrite(args) => embedded_hal::blocking::i2c::Write::write( 147 | machine, 148 | args.address.0, 149 | &args.write_bytes.0, 150 | ) 151 | .map(|_| "".into()), 152 | I2CCommand::I2CRead(args) => { 153 | let mut buffer = vec![0u8; args.read_count]; 154 | embedded_hal::blocking::i2c::Read::read(machine, args.address.0, &mut buffer)?; 155 | Ok(format!("{:02x?}", &buffer)) 156 | } 157 | I2CCommand::WriteRead(args) => { 158 | let mut buffer = vec![0u8; args.read_count]; 159 | embedded_hal::blocking::i2c::WriteRead::write_read( 160 | machine, 161 | args.address.0, 162 | &args.write_bytes.0, 163 | &mut buffer, 164 | )?; 165 | Ok(format!("{:02x?}", &buffer)) 166 | } 167 | I2CCommand::I2CConsole(args) => { 168 | println!("I2C Write console (address: 0x{:02x})", args.address.0); 169 | println!("Provide a comma separated list of bytes (hex) then press enter to execute:"); 170 | loop { 171 | let mut buffer = String::new(); 172 | std::io::stdin().read_line(&mut buffer).unwrap(); 173 | let mut bytes = WriteBytes::from_str(&buffer.trim()).unwrap().0; 174 | embedded_hal::blocking::i2c::Write::write( 175 | machine, 176 | args.address.0, 177 | &mut bytes, 178 | )?; 179 | } 180 | } 181 | }, 182 | PhmCli::Spi(cmd) => match &cmd.command { 183 | SpiCommand::SpiWrite(args) => { 184 | embedded_hal::blocking::spi::Write::write(machine, &args.write_bytes.0) 185 | .map(|_| "".into()) 186 | } 187 | SpiCommand::SpiTransfer(args) => { 188 | let mut buffer = args.write_bytes.0.clone(); 189 | embedded_hal::blocking::spi::Transfer::transfer(machine, &mut buffer) 190 | .map(|bytes| format!("{:02x?}", &bytes)) 191 | } 192 | SpiCommand::SpiConsole => { 193 | println!("SPI Transfer console\nProvide a comma separated list of bytes (hex) then press enter to execute:"); 194 | loop { 195 | let mut buffer = String::new(); 196 | std::io::stdin().read_line(&mut buffer).unwrap(); 197 | let mut bytes = WriteBytes::from_str(&buffer.trim()).unwrap().0; 198 | match embedded_hal::blocking::spi::Transfer::transfer(machine, &mut bytes) { 199 | Ok(bytes) => println!("{:02x?}", &bytes), 200 | Err(err) => eprintln!("{:?}", err), 201 | } 202 | } 203 | } 204 | }, 205 | PhmCli::Uart(cmd) => match &cmd.command { 206 | UartCommand::UartWrite(args) => { 207 | embedded_hal::blocking::serial::Write::bwrite_all(machine, &args.write_bytes.0) 208 | .map(|_| "".into()) 209 | } 210 | UartCommand::UartConsole => { 211 | println!("UART TX console\nProvide a comma separated list of bytes (hex) then press enter to execute:"); 212 | loop { 213 | let mut buffer = String::new(); 214 | std::io::stdin().read_line(&mut buffer).unwrap(); 215 | let bytes = WriteBytes::from_str(&buffer.trim()).unwrap().0; 216 | embedded_hal::blocking::serial::Write::bwrite_all(machine, &bytes)?; 217 | } 218 | } 219 | UartCommand::UartListen => { 220 | use std::io::Write; 221 | println!("UART RX console"); 222 | loop { 223 | while let Ok(b) = embedded_hal::serial::Read::::read(machine) { 224 | print!("{:02x} ", b); 225 | } 226 | std::io::stdout().flush().unwrap(); 227 | std::thread::sleep(std::time::Duration::from_millis(10)); 228 | } 229 | } 230 | }, 231 | } 232 | } 233 | } 234 | 235 | impl FromStr for WriteBytes { 236 | type Err = ParseIntError; 237 | 238 | fn from_str(s: &str) -> Result { 239 | let mut bytes: Vec = Vec::new(); 240 | for b in s.split(',') { 241 | let without_prefix = b.trim().trim_start_matches("0x"); 242 | let byte = u8::from_str_radix(without_prefix, 16)?; 243 | bytes.push(byte); 244 | } 245 | 246 | Ok(Self(bytes)) 247 | } 248 | } 249 | 250 | impl FromStr for Address { 251 | type Err = ParseIntError; 252 | 253 | fn from_str(s: &str) -> Result { 254 | let without_prefix = s.trim_start_matches("0x"); 255 | let byte = u8::from_str_radix(without_prefix, 16)?; 256 | 257 | Ok(Self(byte)) 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /host/phm-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use phm::Machine; 3 | use std::time::Duration; 4 | 5 | use crate::cli::PhmCli; 6 | 7 | mod cli; 8 | 9 | fn main() -> Result<(), Box> { 10 | let cmd = PhmCli::parse(); 11 | 12 | let mut dport = None; 13 | 14 | for port in serialport::available_ports().unwrap() { 15 | if let serialport::SerialPortType::UsbPort(serialport::UsbPortInfo { 16 | serial_number: Some(sn), 17 | .. 18 | }) = &port.port_type 19 | { 20 | if sn.as_str() == "ajm123" { 21 | dport = Some(port.clone()); 22 | break; 23 | } 24 | } 25 | } 26 | 27 | let dport = if let Some(port) = dport { 28 | port 29 | } else { 30 | eprintln!("Error: No `Pretty hal machine` connected!"); 31 | return Ok(()); 32 | }; 33 | 34 | let port = serialport::new(dport.port_name, 115200) 35 | .timeout(Duration::from_millis(5)) 36 | .open() 37 | .map_err(|_| "Error: failed to create port")?; 38 | 39 | let mut ehal = Machine::from_port(port).unwrap(); 40 | 41 | match cmd.run(&mut ehal) { 42 | Ok(out) => { 43 | println!("{out}"); 44 | Ok(()) 45 | } 46 | Err(e) => Err(e.into()), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /host/phm/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "CoreFoundation-sys" 7 | version = "0.1.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b" 10 | dependencies = [ 11 | "libc", 12 | "mach 0.1.2", 13 | ] 14 | 15 | [[package]] 16 | name = "IOKit-sys" 17 | version = "0.1.5" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a" 20 | dependencies = [ 21 | "CoreFoundation-sys", 22 | "libc", 23 | "mach 0.1.2", 24 | ] 25 | 26 | [[package]] 27 | name = "aho-corasick" 28 | version = "0.7.18" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 31 | dependencies = [ 32 | "memchr", 33 | ] 34 | 35 | [[package]] 36 | name = "atomic-polyfill" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "e686d748538a32325b28d6411dd8a939e7ad5128e5d0023cc4fd3573db456042" 40 | dependencies = [ 41 | "critical-section", 42 | "riscv-target", 43 | ] 44 | 45 | [[package]] 46 | name = "bare-metal" 47 | version = "0.2.5" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 50 | dependencies = [ 51 | "rustc_version", 52 | ] 53 | 54 | [[package]] 55 | name = "bare-metal" 56 | version = "1.0.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 59 | 60 | [[package]] 61 | name = "bit_field" 62 | version = "0.10.1" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 65 | 66 | [[package]] 67 | name = "bitfield" 68 | version = "0.13.2" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" 71 | 72 | [[package]] 73 | name = "bitflags" 74 | version = "1.3.2" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 77 | 78 | [[package]] 79 | name = "byteorder" 80 | version = "1.4.3" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 83 | 84 | [[package]] 85 | name = "cc" 86 | version = "1.0.72" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 89 | 90 | [[package]] 91 | name = "cfg-if" 92 | version = "0.1.10" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 95 | 96 | [[package]] 97 | name = "cfg-if" 98 | version = "1.0.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 101 | 102 | [[package]] 103 | name = "cortex-m" 104 | version = "0.7.4" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826" 107 | dependencies = [ 108 | "bare-metal 0.2.5", 109 | "bitfield", 110 | "embedded-hal", 111 | "volatile-register", 112 | ] 113 | 114 | [[package]] 115 | name = "critical-section" 116 | version = "0.2.5" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" 119 | dependencies = [ 120 | "bare-metal 1.0.0", 121 | "cfg-if 1.0.0", 122 | "cortex-m", 123 | "riscv", 124 | ] 125 | 126 | [[package]] 127 | name = "embedded-hal" 128 | version = "0.2.6" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" 131 | dependencies = [ 132 | "nb 0.1.3", 133 | "void", 134 | ] 135 | 136 | [[package]] 137 | name = "hash32" 138 | version = "0.2.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 141 | dependencies = [ 142 | "byteorder", 143 | ] 144 | 145 | [[package]] 146 | name = "heapless" 147 | version = "0.7.10" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" 150 | dependencies = [ 151 | "atomic-polyfill", 152 | "hash32", 153 | "serde", 154 | "spin", 155 | "stable_deref_trait", 156 | ] 157 | 158 | [[package]] 159 | name = "lazy_static" 160 | version = "1.4.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 163 | 164 | [[package]] 165 | name = "libc" 166 | version = "0.2.117" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" 169 | 170 | [[package]] 171 | name = "libudev" 172 | version = "0.2.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe" 175 | dependencies = [ 176 | "libc", 177 | "libudev-sys", 178 | ] 179 | 180 | [[package]] 181 | name = "libudev-sys" 182 | version = "0.1.4" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 185 | dependencies = [ 186 | "libc", 187 | "pkg-config", 188 | ] 189 | 190 | [[package]] 191 | name = "lock_api" 192 | version = "0.4.6" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 195 | dependencies = [ 196 | "scopeguard", 197 | ] 198 | 199 | [[package]] 200 | name = "mach" 201 | version = "0.1.2" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" 204 | dependencies = [ 205 | "libc", 206 | ] 207 | 208 | [[package]] 209 | name = "mach" 210 | version = "0.2.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" 213 | dependencies = [ 214 | "libc", 215 | ] 216 | 217 | [[package]] 218 | name = "memchr" 219 | version = "2.4.1" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 222 | 223 | [[package]] 224 | name = "nb" 225 | version = "0.1.3" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 228 | dependencies = [ 229 | "nb 1.0.0", 230 | ] 231 | 232 | [[package]] 233 | name = "nb" 234 | version = "1.0.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 237 | 238 | [[package]] 239 | name = "nix" 240 | version = "0.16.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb" 243 | dependencies = [ 244 | "bitflags", 245 | "cc", 246 | "cfg-if 0.1.10", 247 | "libc", 248 | "void", 249 | ] 250 | 251 | [[package]] 252 | name = "phm" 253 | version = "0.0.1" 254 | dependencies = [ 255 | "embedded-hal", 256 | "heapless", 257 | "phm-icd", 258 | "postcard", 259 | "serde", 260 | "serialport", 261 | ] 262 | 263 | [[package]] 264 | name = "phm-icd" 265 | version = "0.0.1" 266 | dependencies = [ 267 | "heapless", 268 | "serde", 269 | ] 270 | 271 | [[package]] 272 | name = "pkg-config" 273 | version = "0.3.24" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 276 | 277 | [[package]] 278 | name = "postcard" 279 | version = "0.7.3" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" 282 | dependencies = [ 283 | "heapless", 284 | "postcard-cobs", 285 | "serde", 286 | ] 287 | 288 | [[package]] 289 | name = "postcard-cobs" 290 | version = "0.1.5-pre" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" 293 | 294 | [[package]] 295 | name = "proc-macro2" 296 | version = "1.0.36" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 299 | dependencies = [ 300 | "unicode-xid", 301 | ] 302 | 303 | [[package]] 304 | name = "quote" 305 | version = "1.0.15" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 308 | dependencies = [ 309 | "proc-macro2", 310 | ] 311 | 312 | [[package]] 313 | name = "regex" 314 | version = "1.5.4" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 317 | dependencies = [ 318 | "aho-corasick", 319 | "memchr", 320 | "regex-syntax", 321 | ] 322 | 323 | [[package]] 324 | name = "regex-syntax" 325 | version = "0.6.25" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 328 | 329 | [[package]] 330 | name = "riscv" 331 | version = "0.7.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" 334 | dependencies = [ 335 | "bare-metal 1.0.0", 336 | "bit_field", 337 | "riscv-target", 338 | ] 339 | 340 | [[package]] 341 | name = "riscv-target" 342 | version = "0.1.2" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 345 | dependencies = [ 346 | "lazy_static", 347 | "regex", 348 | ] 349 | 350 | [[package]] 351 | name = "rustc_version" 352 | version = "0.2.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 355 | dependencies = [ 356 | "semver", 357 | ] 358 | 359 | [[package]] 360 | name = "scopeguard" 361 | version = "1.1.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 364 | 365 | [[package]] 366 | name = "semver" 367 | version = "0.9.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 370 | dependencies = [ 371 | "semver-parser", 372 | ] 373 | 374 | [[package]] 375 | name = "semver-parser" 376 | version = "0.7.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 379 | 380 | [[package]] 381 | name = "serde" 382 | version = "1.0.136" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 385 | dependencies = [ 386 | "serde_derive", 387 | ] 388 | 389 | [[package]] 390 | name = "serde_derive" 391 | version = "1.0.136" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 394 | dependencies = [ 395 | "proc-macro2", 396 | "quote", 397 | "syn", 398 | ] 399 | 400 | [[package]] 401 | name = "serialport" 402 | version = "4.0.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "5d8cd7c0f22290ee2c01457009fa6fc1cae4153d5608a924e5dc423babc2c655" 405 | dependencies = [ 406 | "CoreFoundation-sys", 407 | "IOKit-sys", 408 | "bitflags", 409 | "cfg-if 0.1.10", 410 | "libudev", 411 | "mach 0.2.3", 412 | "nix", 413 | "regex", 414 | "winapi", 415 | ] 416 | 417 | [[package]] 418 | name = "spin" 419 | version = "0.9.2" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" 422 | dependencies = [ 423 | "lock_api", 424 | ] 425 | 426 | [[package]] 427 | name = "stable_deref_trait" 428 | version = "1.2.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 431 | 432 | [[package]] 433 | name = "syn" 434 | version = "1.0.86" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 437 | dependencies = [ 438 | "proc-macro2", 439 | "quote", 440 | "unicode-xid", 441 | ] 442 | 443 | [[package]] 444 | name = "unicode-xid" 445 | version = "0.2.2" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 448 | 449 | [[package]] 450 | name = "vcell" 451 | version = "0.1.3" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 454 | 455 | [[package]] 456 | name = "void" 457 | version = "1.0.2" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 460 | 461 | [[package]] 462 | name = "volatile-register" 463 | version = "0.2.1" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" 466 | dependencies = [ 467 | "vcell", 468 | ] 469 | 470 | [[package]] 471 | name = "winapi" 472 | version = "0.3.9" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 475 | dependencies = [ 476 | "winapi-i686-pc-windows-gnu", 477 | "winapi-x86_64-pc-windows-gnu", 478 | ] 479 | 480 | [[package]] 481 | name = "winapi-i686-pc-windows-gnu" 482 | version = "0.4.0" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 485 | 486 | [[package]] 487 | name = "winapi-x86_64-pc-windows-gnu" 488 | version = "0.4.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 491 | -------------------------------------------------------------------------------- /host/phm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["James Munns "] 3 | categories = ["embedded"] 4 | description = "Pretty HAL Machine" 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | name = "phm" 8 | readme = "../../README.md" 9 | repository = "https://github.com/jamesmunns/pretty-hal-machine" 10 | version = "0.0.2" 11 | 12 | [dependencies] 13 | embedded-hal = "0.2.6" 14 | nb = "1.0.0" 15 | serde = "1.0.136" 16 | serialport = "4.0.1" 17 | 18 | [dependencies.heapless] 19 | features = ["serde"] 20 | version = "0.7.10" 21 | 22 | [dependencies.phm-icd] 23 | path = "../../common/phm-icd" 24 | version = "0.0.2" 25 | 26 | [dependencies.postcard] 27 | features = ["use-std"] 28 | version = "0.7.3" 29 | -------------------------------------------------------------------------------- /host/phm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use phm_icd::{ToMcu, ToMcuI2c, ToMcuSpi, ToMcuUart, ToPc, ToPcI2c, ToPcSpi, ToPcUart}; 2 | use postcard::{to_stdvec_cobs, CobsAccumulator, FeedResult}; 3 | use serialport::SerialPort; 4 | use std::{ 5 | collections::VecDeque, 6 | fmt::Display, 7 | io::{self, ErrorKind}, 8 | time::{Duration, Instant}, 9 | }; 10 | 11 | /// The Pretty HAL Machine 12 | /// 13 | /// This wraps a serial port connection to an embedded machine, 14 | /// and implements various [embedded-hal](embedded-hal) traits. 15 | pub struct Machine { 16 | port: Box, 17 | cobs_buf: CobsAccumulator<512>, 18 | command_timeout: Duration, 19 | uart_rx_buf: VecDeque, 20 | } 21 | 22 | /// The main Error type 23 | #[derive(Debug)] 24 | pub enum Error { 25 | PhmSerial(io::Error), 26 | Postcard(postcard::Error), 27 | Timeout(Duration), 28 | 29 | // TODO: This probably needs some more context/nuance... 30 | ResponseError, 31 | InvalidParameter, 32 | Unknown, 33 | } 34 | 35 | impl From for Error { 36 | fn from(err: postcard::Error) -> Self { 37 | Error::Postcard(err) 38 | } 39 | } 40 | 41 | impl From for Error { 42 | fn from(err: io::Error) -> Self { 43 | Error::PhmSerial(err) 44 | } 45 | } 46 | 47 | impl Display for Error { 48 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 49 | match self { 50 | Error::PhmSerial(e) => { 51 | write!(f, "PhmSerialError: {}", e) 52 | } 53 | Error::Postcard(e) => { 54 | write!(f, "PostcardError: {}", e) 55 | } 56 | Error::Timeout(d) => { 57 | write!(f, "Timeout({:?})", d) 58 | } 59 | Error::ResponseError => { 60 | write!(f, "ResponseError") 61 | } 62 | Error::InvalidParameter => { 63 | write!(f, "InvalidParameterError") 64 | } 65 | Error::Unknown => { 66 | write!(f, "UnknownError") 67 | } 68 | } 69 | } 70 | } 71 | 72 | impl std::error::Error for Error {} 73 | 74 | impl Machine { 75 | pub fn from_port(port: Box) -> Result { 76 | // TODO: some kind of sanity checking? Check version, protocol, 77 | // signs of life, anything? 78 | Ok(Self { 79 | port, 80 | cobs_buf: CobsAccumulator::new(), 81 | command_timeout: Duration::from_secs(3), 82 | uart_rx_buf: Default::default(), 83 | }) 84 | } 85 | 86 | /// Set the timeout for a full command to complete. 87 | /// 88 | /// This is not a single message timeout, but rather the timeout 89 | /// for a whole command (e.g. an I2C write) to execute. This is currently 90 | /// only checked/set host side, so endless loops on the embedded side are 91 | /// still possible. 92 | pub fn set_command_timeout(&mut self, timeout: Duration) { 93 | self.command_timeout = timeout; 94 | } 95 | 96 | fn poll(&mut self) -> Result, Error> { 97 | let mut responses = vec![]; 98 | let mut buf = [0u8; 1024]; 99 | 100 | // read from stdin and push it to the decoder 101 | match self.port.read(&mut buf) { 102 | Ok(n) if n > 0 => { 103 | let buf = &buf[..n]; 104 | let mut window = &buf[..]; 105 | 106 | 'cobs: while !window.is_empty() { 107 | window = match self.cobs_buf.feed::>(&window) { 108 | FeedResult::Consumed => break 'cobs, 109 | FeedResult::OverFull(new_wind) => new_wind, 110 | FeedResult::DeserError(new_wind) => new_wind, 111 | FeedResult::Success { data, remaining } => { 112 | // Do something with `data: MyData` here. 113 | if let Ok(data) = data { 114 | responses.push(data); 115 | } else { 116 | return Err(Error::ResponseError); 117 | } 118 | 119 | remaining 120 | } 121 | }; 122 | } 123 | } 124 | Ok(_) => {} 125 | Err(e) if e.kind() == ErrorKind::TimedOut => {} 126 | Err(e) => { 127 | return Err(Error::PhmSerial(e)); 128 | } 129 | } 130 | Ok(responses) 131 | } 132 | } 133 | 134 | impl embedded_hal::blocking::i2c::Write for Machine { 135 | type Error = Error; 136 | 137 | fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { 138 | let msg = ToMcu::I2c(ToMcuI2c::Write { 139 | addr: address, 140 | output: bytes.iter().cloned().collect(), 141 | }); 142 | let ser_msg = to_stdvec_cobs(&msg)?; 143 | self.port.write_all(&ser_msg)?; 144 | 145 | let start = Instant::now(); 146 | 147 | while start.elapsed() < self.command_timeout { 148 | for msg in self.poll()? { 149 | if let ToPc::I2c(ToPcI2c::WriteComplete { addr }) = msg { 150 | if address != addr { 151 | continue; 152 | } 153 | return Ok(()); 154 | } 155 | } 156 | 157 | // TODO: We should probably just use the `timeout` value of the serial 158 | // port, (e.g. don't delay at all), but I guess this is fine for now. 159 | std::thread::sleep(Duration::from_millis(10)); 160 | } 161 | 162 | Err(Error::Timeout(self.command_timeout)) 163 | } 164 | } 165 | 166 | impl embedded_hal::blocking::i2c::Read for Machine { 167 | type Error = Error; 168 | 169 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 170 | let msg = ToMcu::I2c(ToMcuI2c::Read { 171 | addr: address, 172 | to_read: len_to_u32(buffer.len())?, 173 | }); 174 | let ser_msg = to_stdvec_cobs(&msg)?; 175 | self.port.write_all(&ser_msg)?; 176 | 177 | let start = Instant::now(); 178 | 179 | while start.elapsed() < self.command_timeout { 180 | for msg in self.poll()? { 181 | if let ToPc::I2c(ToPcI2c::Read { addr, data_read }) = msg { 182 | if address != addr { 183 | continue; 184 | } 185 | 186 | if data_read.len() != buffer.len() { 187 | return Err(Error::ResponseError); 188 | } else { 189 | buffer.copy_from_slice(&data_read); 190 | return Ok(()); 191 | } 192 | } 193 | } 194 | 195 | // TODO: We should probably just use the `timeout` value of the serial 196 | // port, (e.g. don't delay at all), but I guess this is fine for now. 197 | std::thread::sleep(Duration::from_millis(10)); 198 | } 199 | 200 | Err(Error::Timeout(self.command_timeout)) 201 | } 202 | } 203 | 204 | impl embedded_hal::blocking::i2c::WriteRead for Machine { 205 | type Error = Error; 206 | 207 | fn write_read( 208 | &mut self, 209 | address: u8, 210 | bytes: &[u8], 211 | buffer: &mut [u8], 212 | ) -> Result<(), Self::Error> { 213 | let msg = ToMcu::I2c(ToMcuI2c::WriteThenRead { 214 | addr: address, 215 | output: bytes.iter().cloned().collect(), 216 | to_read: len_to_u32(buffer.len())?, 217 | }); 218 | let ser_msg = to_stdvec_cobs(&msg)?; 219 | self.port.write_all(&ser_msg)?; 220 | 221 | let start = Instant::now(); 222 | 223 | while start.elapsed() < self.command_timeout { 224 | for msg in self.poll()? { 225 | if let ToPc::I2c(ToPcI2c::WriteThenRead { addr, data_read }) = msg { 226 | if address != addr { 227 | continue; 228 | } 229 | 230 | if data_read.len() != buffer.len() { 231 | return Err(Error::ResponseError); 232 | } else { 233 | buffer.copy_from_slice(&data_read); 234 | return Ok(()); 235 | } 236 | } 237 | } 238 | 239 | // TODO: We should probably just use the `timeout` value of the serial 240 | // port, (e.g. don't delay at all), but I guess this is fine for now. 241 | std::thread::sleep(Duration::from_millis(10)); 242 | } 243 | 244 | Err(Error::Timeout(self.command_timeout)) 245 | } 246 | } 247 | 248 | impl embedded_hal::blocking::spi::Write for Machine { 249 | type Error = Error; 250 | 251 | fn write(&mut self, bytes: &[u8]) -> Result<(), Error> { 252 | let msg = ToMcu::Spi(ToMcuSpi::Write { 253 | output: bytes.iter().cloned().collect(), 254 | }); 255 | let ser_msg = to_stdvec_cobs(&msg)?; 256 | self.port.write_all(&ser_msg)?; 257 | 258 | let start = Instant::now(); 259 | 260 | while start.elapsed() < self.command_timeout { 261 | for msg in self.poll()? { 262 | if let ToPc::Spi(ToPcSpi::WriteComplete) = msg { 263 | return Ok(()); 264 | } 265 | } 266 | 267 | // TODO: We should probably just use the `timeout` value of the serial 268 | // port, (e.g. don't delay at all), but I guess this is fine for now. 269 | std::thread::sleep(Duration::from_millis(10)); 270 | } 271 | 272 | Err(Error::Timeout(self.command_timeout)) 273 | } 274 | } 275 | 276 | impl embedded_hal::blocking::spi::Transfer for Machine { 277 | type Error = Error; 278 | 279 | fn transfer<'a>(&mut self, buffer: &'a mut [u8]) -> Result<&'a [u8], Self::Error> { 280 | let msg = ToMcu::Spi(ToMcuSpi::Transfer { 281 | output: buffer.iter().cloned().collect(), 282 | }); 283 | let ser_msg = to_stdvec_cobs(&msg)?; 284 | self.port.write_all(&ser_msg)?; 285 | 286 | let start = Instant::now(); 287 | 288 | while start.elapsed() < self.command_timeout { 289 | for msg in self.poll()? { 290 | if let ToPc::Spi(ToPcSpi::Transfer { data_read }) = msg { 291 | if data_read.len() != buffer.len() { 292 | return Err(Error::ResponseError); 293 | } else { 294 | buffer.copy_from_slice(&data_read); 295 | return Ok(buffer); 296 | } 297 | } 298 | } 299 | 300 | // TODO: We should probably just use the `timeout` value of the serial 301 | // port, (e.g. don't delay at all), but I guess this is fine for now. 302 | std::thread::sleep(Duration::from_millis(10)); 303 | } 304 | 305 | Err(Error::Timeout(self.command_timeout)) 306 | } 307 | } 308 | 309 | impl embedded_hal::blocking::serial::Write for Machine { 310 | type Error = Error; 311 | 312 | fn bwrite_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { 313 | let msg = ToMcu::Uart(ToMcuUart::Write { 314 | output: bytes.iter().cloned().collect(), 315 | }); 316 | let ser_msg = to_stdvec_cobs(&msg)?; 317 | self.port.write_all(&ser_msg)?; 318 | 319 | let start = Instant::now(); 320 | 321 | while start.elapsed() < self.command_timeout { 322 | for msg in self.poll()? { 323 | if let ToPc::Uart(ToPcUart::WriteComplete) = msg { 324 | return Ok(()); 325 | } 326 | } 327 | 328 | // TODO: We should probably just use the `timeout` value of the serial 329 | // port, (e.g. don't delay at all), but I guess this is fine for now. 330 | std::thread::sleep(Duration::from_millis(10)); 331 | } 332 | 333 | Err(Error::Timeout(self.command_timeout)) 334 | } 335 | 336 | fn bflush(&mut self) -> Result<(), Self::Error> { 337 | let msg = ToMcu::Uart(ToMcuUart::Flush); 338 | let ser_msg = to_stdvec_cobs(&msg)?; 339 | self.port.write_all(&ser_msg)?; 340 | 341 | let start = Instant::now(); 342 | 343 | while start.elapsed() < self.command_timeout { 344 | for msg in self.poll()? { 345 | if let ToPc::Uart(ToPcUart::WriteComplete) = msg { 346 | return Ok(()); 347 | } 348 | } 349 | 350 | // TODO: We should probably just use the `timeout` value of the serial 351 | // port, (e.g. don't delay at all), but I guess this is fine for now. 352 | std::thread::sleep(Duration::from_millis(10)); 353 | } 354 | 355 | Err(Error::Timeout(self.command_timeout)) 356 | } 357 | } 358 | 359 | impl embedded_hal::serial::Write for Machine { 360 | type Error = Error; 361 | 362 | fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 363 | embedded_hal::blocking::serial::Write::::bwrite_all(self, &[byte]) 364 | .map_err(|_| nb::Error::WouldBlock) 365 | } 366 | 367 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 368 | embedded_hal::blocking::serial::Write::::bflush(self).map_err(|_| nb::Error::WouldBlock) 369 | } 370 | } 371 | 372 | impl embedded_hal::serial::Read for Machine { 373 | type Error = Error; 374 | 375 | fn read(&mut self) -> nb::Result { 376 | if !self.uart_rx_buf.is_empty() { 377 | Ok(self.uart_rx_buf.pop_front().unwrap()) 378 | } else { 379 | let msg = ToMcu::Uart(ToMcuUart::Read); 380 | let ser_msg = to_stdvec_cobs(&msg).unwrap(); 381 | self.port.write_all(&ser_msg).unwrap(); 382 | 383 | let start = Instant::now(); 384 | 385 | while start.elapsed() < self.command_timeout { 386 | if let Ok(vec) = self.poll() { 387 | for msg in vec { 388 | if let ToPc::Uart(ToPcUart::Read { data_read }) = msg { 389 | self.uart_rx_buf.extend(data_read); 390 | // break 'timeout; 391 | if !self.uart_rx_buf.is_empty() { 392 | return Ok(self.uart_rx_buf.pop_front().unwrap()); 393 | } else { 394 | return Err(nb::Error::WouldBlock); 395 | } 396 | } 397 | } 398 | } 399 | 400 | // TODO: We should probably just use the `timeout` value of the serial 401 | // port, (e.g. don't delay at all), but I guess this is fine for now. 402 | std::thread::sleep(Duration::from_millis(10)); 403 | } 404 | Err(nb::Error::Other(Error::Timeout(self.command_timeout))) 405 | } 406 | } 407 | } 408 | 409 | // TODO: This is overly accepting! We have a much smaller max message size than this. 410 | fn len_to_u32(len: usize) -> Result { 411 | len.try_into().map_err(|_| Error::InvalidParameter) 412 | } 413 | --------------------------------------------------------------------------------