├── .cargo └── config ├── .gdbinit ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bors.toml ├── build.rs ├── ci ├── install.sh └── script.sh ├── core ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── examples ├── blinking_led.rs ├── blinking_led_with_interrupts.rs └── lcd.rs ├── gdb.bat ├── gdb.sh ├── interrupture-stm32f7x6 ├── Cargo.toml └── src │ └── lib.rs ├── interrupture ├── Cargo.toml └── src │ └── lib.rs ├── memory.x ├── rust-toolchain ├── src ├── bin │ ├── async-await.rs │ └── polling.rs ├── ethernet │ ├── init.rs │ ├── mod.rs │ ├── phy.rs │ ├── rx.rs │ └── tx.rs ├── future_mutex.rs ├── gpio │ ├── mod.rs │ ├── port.rs │ └── traits.rs ├── i2c.rs ├── init │ ├── mod.rs │ └── pins.rs ├── interrupts │ ├── mod.rs │ └── primask_mutex.rs ├── lcd │ ├── color.rs │ ├── font.rs │ ├── init.rs │ ├── mod.rs │ └── stdout.rs ├── lib.rs ├── mpsc_queue.rs ├── random.rs ├── sd │ ├── error.rs │ ├── init.rs │ ├── mod.rs │ └── sdmmc_cmd.rs ├── system_clock.rs ├── task_runtime.rs ├── task_runtime │ ├── mpsc.rs │ └── mpsc │ │ └── queue.rs └── touch.rs └── stlink-1.3.1-win32.zip /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" 3 | 4 | [target.thumbv7em-none-eabihf] 5 | runner = "./gdb.sh" 6 | rustflags = [ 7 | "-C", "link-arg=-Tlink.x", 8 | "-C", "linker=rust-lld", 9 | "-C", "linker-flavor=ld.lld", 10 | ] 11 | -------------------------------------------------------------------------------- /.gdbinit: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | # print demangled symbols by default 4 | set print asm-demangle on 5 | 6 | monitor arm semihosting enable 7 | 8 | # # send captured ITM to the file itm.fifo 9 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 10 | # # 8000000 must match the core clock frequency 11 | # monitor tpiu config internal itm.fifo uart off 8000000 12 | 13 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 14 | # # 2000000 is the frequency of the SWO pin 15 | # monitor tpiu config external uart off 8000000 2000000 16 | 17 | # # enable ITM port 0 18 | # monitor itm port 0 on 19 | 20 | load 21 | step 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | blinking_led/target 3 | *.rs.bk 4 | interrupture/Cargo.lock 5 | interrupture-stm32f7x6/Cargo.lock 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | os: 4 | - linux 5 | - mac 6 | - windows 7 | 8 | rust: nightly 9 | 10 | env: TARGET=thumbv7em-none-eabihf 11 | 12 | matrix: 13 | allow_failures: 14 | - os: windows # see https://travis-ci.community/t/rust-error-could-not-compile-winapi/268 15 | 16 | before_install: set -e 17 | 18 | install: 19 | - bash ci/install.sh 20 | 21 | script: 22 | - bash ci/script.sh 23 | 24 | after_script: set +e 25 | 26 | # cache: cargo 27 | 28 | before_cache: 29 | # Travis can't cache files that are not readable by "others" 30 | - chmod -R a+r $HOME/.cargo 31 | 32 | branches: 33 | only: 34 | - staging 35 | - trying 36 | - master 37 | 38 | notifications: 39 | email: 40 | on_success: never 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["default-run"] 2 | 3 | [package] 4 | authors = ["Philipp Oppermann "] 5 | categories = ["embedded", "no-std"] 6 | license = "MIT OR Apache-2.0" 7 | name = "stm32f7-discovery" 8 | version = "0.1.0" 9 | edition = "2018" 10 | default-run = "polling" 11 | 12 | [dependencies] 13 | cortex-m = "0.5.2" 14 | cortex-m-rt = "0.6.4" 15 | cortex-m-semihosting = "0.3.0" 16 | alloc-cortex-m = "0.3.4" 17 | spin = "0.4.8" 18 | bitflags = "1.0.3" 19 | volatile = "0.2.4" 20 | bit_field = "0.9.0" 21 | bare-metal = "0.2.3" 22 | embedded-hal = "0.2.1" 23 | pin-utils = "0.1.0-alpha" 24 | core = {path = "core"} 25 | 26 | [dependencies.stm32f7] 27 | version = "0.3.2" 28 | features = ["stm32f7x6", "rt"] 29 | 30 | [dependencies.arrayvec] 31 | version = "0.4.7" 32 | default-features = false 33 | 34 | [dependencies.byteorder] 35 | version = "1.0" 36 | default-features = false 37 | 38 | [dependencies.smoltcp] 39 | #version = "0.5.0" 40 | git = "https://github.com/oli-obk/smoltcp.git" 41 | branch = "patch-2" 42 | default-features = false 43 | features = ["alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "proto-ipv4", "proto-dhcpv4"] 44 | 45 | [dependencies.font8x8] 46 | version = "0.2.4" 47 | default-features = false 48 | features = ["unicode"] 49 | 50 | [dependencies.futures-preview] 51 | git = "https://github.com/rust-lang-nursery/futures-rs.git" 52 | default-features = false 53 | features = ["alloc", "nightly"] 54 | 55 | [profile.release] 56 | codegen-units = 1 # better optimizations 57 | debug = true 58 | lto = true # better optimizations 59 | incremental = false # TODO: remove after https://github.com/rust-lang/cargo/pull/6610 lands in nightly 60 | 61 | [patch.crates-io.cortex-m-rt] 62 | # TODO: replace with crates.io version when new version is released 63 | git = "https://github.com/rust-embedded/cortex-m-rt.git" 64 | 65 | [dependencies.interrupture-stm32f7x6] 66 | path = "interrupture-stm32f7x6" 67 | version = "0.1.0" 68 | -------------------------------------------------------------------------------- /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 | Copyright (c) 2018 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stm32f7-discovery 2 | 3 | ## Building 4 | 5 | - **Install the thumbv7em-none-eabihf target**: Run `rustup target add thumbv7em-none-eabihf`. 6 | - **Run `cargo build`** 7 | 8 | ## Running 9 | 10 | First you need to install some dependencies: 11 | 12 | - **Install stlink**: See . 13 | - **Install openocd**: At least version 0.10.0 is needed. You can install it either from your package manager or [from source](https://sourceforge.net/projects/openocd/). 14 | - **Install gdb-multiarch**: This cross-platform version of GDB should be available through your package manager. 15 | 16 | Then you can connect your controller and run the following: 17 | 18 | - **Start openocd**: In a separate terminal window, run `openocd -f board/stm32f7discovery.cfg`. You might need `sudo`. If you get an "Can't find board/stm32f7discovery.cfg" error your version of openocd might be too old (it should be at least 0.10.0). 19 | - **Run `cargo run`**: This connects to the openocd instance and flashes your binary to the controller. 20 | - **Continue execution**: By default GDB pauses the execution after loading. To continue your program, run `continue` or `c`. 21 | 22 | To run in release mode (i.e. with optimizations), run `cargo run --release`. 23 | 24 | ## License 25 | 26 | Licensed under either of 27 | 28 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 29 | http://www.apache.org/licenses/LICENSE-2.0) 30 | 31 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 32 | 33 | at your option. 34 | 35 | ## Contribution 36 | 37 | Unless you explicitly state otherwise, any contribution intentionally submitted 38 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 39 | dual licensed as above, without any additional terms or conditions. 40 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push", 3 | ] -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | // Put the linker script somewhere the linker can find it 8 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 9 | File::create(out.join("memory.x")) 10 | .unwrap() 11 | .write_all(include_bytes!("memory.x")) 12 | .unwrap(); 13 | println!("cargo:rustc-link-search={}", out.display()); 14 | 15 | println!("cargo:rerun-if-changed=build.rs"); 16 | println!("cargo:rerun-if-changed=memory.x"); 17 | } 18 | -------------------------------------------------------------------------------- /ci/install.sh: -------------------------------------------------------------------------------- 1 | set -euxo pipefail 2 | 3 | main() { 4 | rustup target add $TARGET 5 | } 6 | 7 | main 8 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | set -euxo pipefail 2 | 3 | main() { 4 | cargo build 5 | cargo build --release 6 | cargo build --examples 7 | cargo build --examples --release 8 | } 9 | 10 | main 11 | -------------------------------------------------------------------------------- /core/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "core" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core" 3 | version = "0.1.0" 4 | authors = ["Philipp Oppermann "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(optin_builtin_traits)] 2 | #![feature(generator_trait)] 3 | #![feature(arbitrary_self_types)] 4 | #![no_std] 5 | 6 | pub use core::*; 7 | 8 | pub mod future { 9 | pub use core::future::*; 10 | 11 | use core::{ 12 | ops::{Generator, GeneratorState}, 13 | pin::Pin, 14 | ptr, 15 | sync::atomic::{AtomicPtr, Ordering}, 16 | task::{Waker, Poll, Context}, 17 | }; 18 | 19 | /// Wrap a future in a generator. 20 | /// 21 | /// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give 22 | /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). 23 | pub fn from_generator>(x: T) -> impl Future { 24 | GenFuture(x) 25 | } 26 | 27 | /// A wrapper around generators used to implement `Future` for `async`/`await` code. 28 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 29 | #[must_use = "futures do nothing unless polled"] 30 | struct GenFuture>(T); 31 | 32 | // We rely on the fact that async/await futures are immovable in order to create 33 | // self-referential borrows in the underlying generator. 34 | impl> !Unpin for GenFuture {} 35 | 36 | impl> Future for GenFuture { 37 | type Output = T::Return; 38 | fn poll(self: Pin<&mut Self>, lw: &mut Context) -> Poll { 39 | // Safe because we're !Unpin + !Drop mapping to a ?Unpin value 40 | let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; 41 | set_task_waker(lw.waker(), || match gen.resume() { 42 | GeneratorState::Yielded(()) => Poll::Pending, 43 | GeneratorState::Complete(x) => Poll::Ready(x), 44 | }) 45 | } 46 | } 47 | 48 | // FIXME: Should be thread local, but is currently a static since we only have a single thread 49 | static TLS_WAKER: AtomicPtr = AtomicPtr::new(ptr::null_mut()); 50 | 51 | struct SetOnDrop(*mut Waker); 52 | 53 | impl Drop for SetOnDrop { 54 | fn drop(&mut self) { 55 | TLS_WAKER.store(self.0, Ordering::SeqCst); 56 | } 57 | } 58 | 59 | /// Sets the thread-local task context used by async/await futures. 60 | pub fn set_task_waker(lw: &Waker, f: F) -> R 61 | where 62 | F: FnOnce() -> R, 63 | { 64 | let old_waker = TLS_WAKER.swap(lw as *const _ as *mut _, Ordering::SeqCst); 65 | let _reset_waker = SetOnDrop(old_waker); 66 | f() 67 | } 68 | 69 | /// Retrieves the thread-local task waker used by async/await futures. 70 | /// 71 | /// This function acquires exclusive access to the task waker. 72 | /// 73 | /// Panics if no waker has been set or if the waker has already been 74 | /// retrieved by a surrounding call to get_task_waker. 75 | pub fn get_task_waker(f: F) -> R 76 | where 77 | F: FnOnce(&Waker) -> R, 78 | { 79 | // Clear the entry so that nested `get_task_waker` calls 80 | // will fail or set their own value. 81 | let waker_ptr = TLS_WAKER.swap(ptr::null_mut(), Ordering::SeqCst); 82 | let _reset_waker = SetOnDrop(waker_ptr); 83 | 84 | let waker_ptr = unsafe { waker_ptr.as_ref() }.expect("TLS Waker not set."); 85 | f(waker_ptr) 86 | } 87 | 88 | /// Polls a future in the current thread-local task waker. 89 | pub fn poll_with_tls_waker(f: Pin<&mut F>) -> Poll 90 | where 91 | F: Future, 92 | { 93 | get_task_waker(|lw| F::poll(f, &mut Context::from_waker(lw))) 94 | } 95 | 96 | #[macro_export] 97 | macro_rules! r#await { 98 | ($e:expr) => {{ 99 | let mut pinned = $e; 100 | loop { 101 | if let core::task::Poll::Ready(x) = $crate::future::poll_with_tls_waker(unsafe { 102 | core::pin::Pin::new_unchecked(&mut pinned) 103 | }) { 104 | break x; 105 | } 106 | // FIXME(cramertj) prior to stabilizing await, we have to ensure that this 107 | // can't be used to create a generator on stable via `|| await!()`. 108 | yield 109 | } 110 | }}; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /examples/blinking_led.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | 5 | use alloc_cortex_m::CortexMHeap; 6 | use core::alloc::Layout as AllocLayout; 7 | use core::panic::PanicInfo; 8 | use cortex_m_rt::{entry, exception}; 9 | use stm32f7::stm32f7x6::{CorePeripherals, Peripherals}; 10 | use stm32f7_discovery::{ 11 | gpio::{GpioPort, OutputPin}, 12 | init, 13 | system_clock::{self, Hz}, 14 | }; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let core_peripherals = CorePeripherals::take().unwrap(); 19 | let mut systick = core_peripherals.SYST; 20 | 21 | let peripherals = Peripherals::take().unwrap(); 22 | let mut rcc = peripherals.RCC; 23 | let mut pwr = peripherals.PWR; 24 | let mut flash = peripherals.FLASH; 25 | 26 | init::init_system_clock_216mhz(&mut rcc, &mut pwr, &mut flash); 27 | init::enable_gpio_ports(&mut rcc); 28 | 29 | let gpio_a = GpioPort::new(peripherals.GPIOA); 30 | let gpio_b = GpioPort::new(peripherals.GPIOB); 31 | let gpio_c = GpioPort::new(peripherals.GPIOC); 32 | let gpio_d = GpioPort::new(peripherals.GPIOD); 33 | let gpio_e = GpioPort::new(peripherals.GPIOE); 34 | let gpio_f = GpioPort::new(peripherals.GPIOF); 35 | let gpio_g = GpioPort::new(peripherals.GPIOG); 36 | let gpio_h = GpioPort::new(peripherals.GPIOH); 37 | let gpio_i = GpioPort::new(peripherals.GPIOI); 38 | let gpio_j = GpioPort::new(peripherals.GPIOJ); 39 | let gpio_k = GpioPort::new(peripherals.GPIOK); 40 | let mut pins = init::pins( 41 | gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g, gpio_h, gpio_i, gpio_j, gpio_k, 42 | ); 43 | 44 | // configure the systick timer 20Hz (20 ticks per second) 45 | init::init_systick(Hz(20), &mut systick, &rcc); 46 | systick.enable_interrupt(); 47 | 48 | // turn led on 49 | pins.led.set(true); 50 | 51 | let mut last_led_toggle = system_clock::ticks(); 52 | loop { 53 | let ticks = system_clock::ticks(); 54 | // every 0.5 seconds (we have 20 ticks per second) 55 | if ticks - last_led_toggle >= 10 { 56 | pins.led.toggle(); 57 | last_led_toggle = ticks; 58 | } 59 | } 60 | } 61 | 62 | #[global_allocator] 63 | static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); 64 | 65 | #[exception] 66 | fn SysTick() { 67 | system_clock::tick(); 68 | } 69 | 70 | // define what happens in an Out Of Memory (OOM) condition 71 | #[alloc_error_handler] 72 | fn rust_oom(_: AllocLayout) -> ! { 73 | loop {} 74 | } 75 | 76 | #[panic_handler] 77 | fn panic(info: &PanicInfo) -> ! { 78 | use core::fmt::Write; 79 | use cortex_m::asm; 80 | use cortex_m_semihosting::hio; 81 | 82 | if let Ok(mut hstdout) = hio::hstdout() { 83 | let _ = writeln!(hstdout, "{}", info); 84 | } 85 | 86 | // OK to fire a breakpoint here because we know the microcontroller is connected to a debugger 87 | asm::bkpt(); 88 | 89 | loop {} 90 | } 91 | -------------------------------------------------------------------------------- /examples/blinking_led_with_interrupts.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(alloc_error_handler)] 4 | 5 | use alloc_cortex_m::CortexMHeap; 6 | use core::alloc::Layout as AllocLayout; 7 | use core::panic::PanicInfo; 8 | use cortex_m_rt::{entry, exception}; 9 | use stm32f7::stm32f7x6::{CorePeripherals, Peripherals}; 10 | use stm32f7_discovery::{ 11 | gpio::{GpioPort, OutputPin}, 12 | init, 13 | interrupts::{self, InterruptRequest, Priority}, 14 | system_clock::{self, Hz}, 15 | }; 16 | 17 | const HEAP_SIZE: usize = 50 * 1024; // in bytes 18 | 19 | #[entry] 20 | fn main() -> ! { 21 | let core_peripherals = CorePeripherals::take().unwrap(); 22 | let mut systick = core_peripherals.SYST; 23 | let mut nvic = core_peripherals.NVIC; 24 | 25 | let peripherals = Peripherals::take().unwrap(); 26 | let mut rcc = peripherals.RCC; 27 | let mut pwr = peripherals.PWR; 28 | let mut flash = peripherals.FLASH; 29 | let mut tim6 = peripherals.TIM6; 30 | let mut nvic_stir = peripherals.NVIC_STIR; 31 | 32 | init::init_system_clock_216mhz(&mut rcc, &mut pwr, &mut flash); 33 | init::enable_gpio_ports(&mut rcc); 34 | 35 | let gpio_a = GpioPort::new(peripherals.GPIOA); 36 | let gpio_b = GpioPort::new(peripherals.GPIOB); 37 | let gpio_c = GpioPort::new(peripherals.GPIOC); 38 | let gpio_d = GpioPort::new(peripherals.GPIOD); 39 | let gpio_e = GpioPort::new(peripherals.GPIOE); 40 | let gpio_f = GpioPort::new(peripherals.GPIOF); 41 | let gpio_g = GpioPort::new(peripherals.GPIOG); 42 | let gpio_h = GpioPort::new(peripherals.GPIOH); 43 | let gpio_i = GpioPort::new(peripherals.GPIOI); 44 | let gpio_j = GpioPort::new(peripherals.GPIOJ); 45 | let gpio_k = GpioPort::new(peripherals.GPIOK); 46 | let mut pins = init::pins( 47 | gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g, gpio_h, gpio_i, gpio_j, gpio_k, 48 | ); 49 | 50 | // configure the systick timer 20Hz (20 ticks per second) 51 | init::init_systick(Hz(20), &mut systick, &rcc); 52 | systick.enable_interrupt(); 53 | 54 | // turn led on 55 | pins.led.set(true); 56 | 57 | // enable timers 58 | rcc.apb1enr.modify(|_, w| w.tim6en().enabled()); 59 | 60 | // configure timer 61 | // clear update event 62 | tim6.sr.modify(|_, w| w.uif().clear_bit()); 63 | 64 | // setup timing 65 | tim6.psc.modify(|_, w| unsafe { w.psc().bits(42000) }); 66 | tim6.arr.modify(|_, w| unsafe { w.arr().bits(3000) }); 67 | 68 | // enable interrupt 69 | tim6.dier.modify(|_, w| w.uie().set_bit()); 70 | // start the timer counter 71 | tim6.cr1.modify(|_, w| w.cen().set_bit()); 72 | 73 | // The interrupt module needs an allocator for its dynamic interrupt table. 74 | unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } 75 | 76 | interrupts::scope( 77 | &mut nvic, 78 | &mut nvic_stir, 79 | |_| {}, 80 | |interrupt_table| { 81 | let _ = interrupt_table.register(InterruptRequest::TIM6_DAC, Priority::P1, || { 82 | pins.led.toggle(); 83 | let tim = &mut tim6; 84 | // make sure the interrupt doesn't just restart again by clearing the flag 85 | tim.sr.modify(|_, w| w.uif().clear_bit()); 86 | }); 87 | 88 | loop {} 89 | }, 90 | ) 91 | } 92 | 93 | #[global_allocator] 94 | static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); 95 | 96 | #[exception] 97 | fn SysTick() { 98 | system_clock::tick(); 99 | } 100 | 101 | // define what happens in an Out Of Memory (OOM) condition 102 | #[alloc_error_handler] 103 | fn rust_oom(_: AllocLayout) -> ! { 104 | loop {} 105 | } 106 | 107 | #[panic_handler] 108 | fn panic(info: &PanicInfo) -> ! { 109 | use core::fmt::Write; 110 | use cortex_m::asm; 111 | use cortex_m_semihosting::hio; 112 | 113 | if let Ok(mut hstdout) = hio::hstdout() { 114 | let _ = writeln!(hstdout, "{}", info); 115 | } 116 | 117 | // OK to fire a breakpoint here because we know the microcontroller is connected to a debugger 118 | asm::bkpt(); 119 | 120 | loop {} 121 | } 122 | -------------------------------------------------------------------------------- /examples/lcd.rs: -------------------------------------------------------------------------------- 1 | #![feature(alloc_error_handler)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | use alloc_cortex_m::CortexMHeap; 6 | use core::alloc::Layout as AllocLayout; 7 | use core::fmt::Write; 8 | use core::panic::PanicInfo; 9 | use cortex_m::{asm, interrupt, peripheral::NVIC}; 10 | use cortex_m_rt::{entry, exception, ExceptionFrame}; 11 | use cortex_m_semihosting::hio::{self, HStdout}; 12 | use stm32f7::{ 13 | interrupt, 14 | stm32f7x6::{CorePeripherals, Interrupt, Peripherals} 15 | }; 16 | use stm32f7_discovery::{ 17 | print, println, 18 | gpio::{GpioPort, InputPin, OutputPin}, 19 | init, 20 | lcd, 21 | }; 22 | 23 | #[global_allocator] 24 | static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); 25 | 26 | const HEAP_SIZE: usize = 50 * 1024; // in bytes 27 | 28 | #[entry] 29 | fn main() -> ! { 30 | let core_peripherals = CorePeripherals::take().unwrap(); 31 | let mut nvic = core_peripherals.NVIC; 32 | 33 | let peripherals = Peripherals::take().unwrap(); 34 | let mut rcc = peripherals.RCC; 35 | let mut pwr = peripherals.PWR; 36 | let mut flash = peripherals.FLASH; 37 | let mut fmc = peripherals.FMC; 38 | let mut ltdc = peripherals.LTDC; 39 | 40 | init::init_system_clock_216mhz(&mut rcc, &mut pwr, &mut flash); 41 | init::enable_gpio_ports(&mut rcc); 42 | 43 | let gpio_a = GpioPort::new(peripherals.GPIOA); 44 | let gpio_b = GpioPort::new(peripherals.GPIOB); 45 | let gpio_c = GpioPort::new(peripherals.GPIOC); 46 | let gpio_d = GpioPort::new(peripherals.GPIOD); 47 | let gpio_e = GpioPort::new(peripherals.GPIOE); 48 | let gpio_f = GpioPort::new(peripherals.GPIOF); 49 | let gpio_g = GpioPort::new(peripherals.GPIOG); 50 | let gpio_h = GpioPort::new(peripherals.GPIOH); 51 | let gpio_i = GpioPort::new(peripherals.GPIOI); 52 | let gpio_j = GpioPort::new(peripherals.GPIOJ); 53 | let gpio_k = GpioPort::new(peripherals.GPIOK); 54 | let mut pins = init::pins( 55 | gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g, gpio_h, gpio_i, gpio_j, gpio_k, 56 | ); 57 | 58 | init::init_sdram(&mut rcc, &mut fmc); 59 | let mut lcd = init::init_lcd(&mut ltdc, &mut rcc); 60 | pins.display_enable.set(true); 61 | pins.backlight.set(true); 62 | 63 | let mut layer_1 = lcd.layer_1().unwrap(); 64 | let mut layer_2 = lcd.layer_2().unwrap(); 65 | 66 | layer_1.clear(); 67 | layer_2.clear(); 68 | lcd::init_stdout(layer_2); 69 | 70 | println!("Try pressing the blue button one the left side!"); 71 | 72 | // Initialize the allocator BEFORE you use it 73 | unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } 74 | 75 | nvic.enable(Interrupt::EXTI0); 76 | 77 | let mut previous_button_state = pins.button.get(); 78 | loop { 79 | // poll button state 80 | let current_button_state = pins.button.get(); 81 | if current_button_state != previous_button_state { 82 | if current_button_state { 83 | pins.led.toggle(); 84 | 85 | // trigger the `EXTI0` interrupt 86 | NVIC::pend(Interrupt::EXTI0); 87 | } 88 | 89 | previous_button_state = current_button_state; 90 | } 91 | } 92 | } 93 | 94 | interrupt!(EXTI0, exti0, state: Option = None); 95 | 96 | fn exti0(_state: &mut Option) { 97 | println!("Interrupt fired! This means that the button was pressed."); 98 | } 99 | 100 | #[exception] 101 | fn HardFault(ef: &ExceptionFrame) -> ! { 102 | panic!("HardFault at {:#?}", ef); 103 | } 104 | 105 | // define what happens in an Out Of Memory (OOM) condition 106 | #[alloc_error_handler] 107 | fn rust_oom(_: AllocLayout) -> ! { 108 | panic!("out of memory"); 109 | } 110 | 111 | #[panic_handler] 112 | fn panic(info: &PanicInfo) -> ! { 113 | interrupt::disable(); 114 | 115 | if lcd::stdout::is_initialized() { 116 | println!("{}", info); 117 | } 118 | 119 | if let Ok(mut hstdout) = hio::hstdout() { 120 | let _ = writeln!(hstdout, "{}", info); 121 | } 122 | 123 | // OK to fire a breakpoint here because we know the microcontroller is connected to a debugger 124 | asm::bkpt(); 125 | 126 | loop {} 127 | } 128 | -------------------------------------------------------------------------------- /gdb.bat: -------------------------------------------------------------------------------- 1 | echo "Please run 'st-util' in another terminal window" 2 | echo "" 3 | arm-none-eabi-gdb -iex "add-auto-load-safe-path ." -ex "tar ext :4242" -ex "load-reset" %1 4 | -------------------------------------------------------------------------------- /gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Please run openocd in another terminal window (you might need sudo)" 4 | echo "" 5 | 6 | for GDB in arm-none-eabi-gdb gdb-multiarch 7 | do 8 | command -v "$GDB" >/dev/null && break 9 | done 10 | 11 | exec "$GDB" -iex 'add-auto-load-safe-path .' "$1" 12 | -------------------------------------------------------------------------------- /interrupture-stm32f7x6/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interrupture-stm32f7x6" 3 | version = "0.1.0" 4 | authors = ["Oliver Scherer "] 5 | edition = "2018" 6 | description = "stm32f7x6 support for HAL interrupt usage" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/embed-rs/stm32f7-discovery" 9 | 10 | [dependencies] 11 | interrupture = { version = "0.1.0", path = "../interrupture" } 12 | cortex-m-rt = "0.6.4" 13 | 14 | [dependencies.stm32f7] 15 | version = "0.3.2" 16 | features = ["stm32f7x6", "rt"] 17 | -------------------------------------------------------------------------------- /interrupture-stm32f7x6/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of `interrupture` for `stm32f7x6` 2 | 3 | #![no_std] 4 | #![warn(missing_docs)] 5 | #![deny(clippy::all)] 6 | 7 | use interrupture::handle_isr; 8 | use core::convert::TryFrom; 9 | use cortex_m_rt::exception; 10 | 11 | pub use stm32f7::stm32f7x6::Interrupt as InterruptRequest; 12 | use stm32f7::stm32f7x6::{NVIC, NVIC_STIR}; 13 | use interrupture::Nr; 14 | 15 | /// A convenience wrapper around `interrupture::scope` for `stm32f7x6` 16 | pub fn scope<'a, F, C, R>( 17 | nvic: &'a mut NVIC, 18 | nvic_stir: &'a mut NVIC_STIR, 19 | default_handler: F, 20 | code: C, 21 | ) -> R 22 | where 23 | F: FnMut(u8) + Send, 24 | C: FnOnce(&mut interrupture::InterruptTable<'a, Ic<'a>>) -> R, 25 | { 26 | let ic = Ic { nvic, nvic_stir }; 27 | interrupture::scope(ic, default_handler, code) 28 | } 29 | 30 | #[doc(hidden)] 31 | /// This type only exists for the `InterruptController` trait bound on closure 32 | /// of `scope`, do not use directly, you will never interact with it directly anyway. 33 | pub struct Ic<'a> { 34 | nvic: &'a mut NVIC, 35 | nvic_stir: &'a mut NVIC_STIR, 36 | } 37 | 38 | // HACK: Nr should be more convenient to use (e.g. have some forwarding impls) 39 | struct NrWrap<'a, T: Nr>(&'a T); 40 | unsafe impl<'a, T: Nr> Nr for NrWrap<'a, T> { 41 | fn nr(&self) -> u8 { 42 | self.0.nr() 43 | } 44 | } 45 | 46 | impl<'a> interrupture::InterruptController for Ic<'a> { 47 | type Request = InterruptRequest; 48 | type Priority = Priority; 49 | fn trigger(&mut self, irq: &Self::Request) { 50 | self.nvic_stir 51 | .stir 52 | .write(|w| unsafe { w.intid().bits(irq.nr().into()) }); 53 | } 54 | fn is_pending(irq: &Self::Request) -> bool { 55 | NVIC::is_pending(NrWrap(irq)) 56 | } 57 | fn pend(irq: &Self::Request) { 58 | NVIC::pend(NrWrap(irq)); 59 | } 60 | fn unpend(irq: &Self::Request) { 61 | NVIC::unpend(NrWrap(irq)); 62 | } 63 | fn get_priority(irq: &Self::Request) -> Self::Priority { 64 | let res = NVIC::get_priority(NrWrap(irq)); 65 | 66 | // STM32F7 only uses 4 bits for Priority. priority << 4, because the upper 4 bits are used 67 | // for priority. 68 | match Priority::from_u8(res >> 4) { 69 | Ok(priority) => priority, 70 | Err(PriorityDoesNotExistError(prio_number)) => { 71 | unreachable!("Priority {} does not exist", prio_number) 72 | } 73 | } 74 | } 75 | fn set_priority(&mut self, irq: &Self::Request, priority: Self::Priority) { 76 | // The STM32F7 only supports 16 priority levels 77 | // Assert that priority < 16 78 | // STM32F7 only uses 4 bits for Priority. priority << 4, because the upper 4 bits are used 79 | // for priority. 80 | let priority = (priority as u8) << 4; 81 | 82 | unsafe { self.nvic.set_priority(NrWrap(irq), priority) }; 83 | } 84 | fn disable(&mut self, irq: &Self::Request) { 85 | self.nvic.disable(NrWrap(irq)); 86 | } 87 | fn enable(&mut self, irq: &Self::Request) { 88 | self.nvic.enable(NrWrap(irq)); 89 | } 90 | } 91 | 92 | /// The default interrupt handler that is called for all uncaught IRQs. 93 | #[exception] 94 | fn DefaultHandler(irqn: i16) { 95 | if let Ok(irqn) = u8::try_from(irqn) { 96 | handle_isr(irqn) 97 | } else { 98 | panic!("Unhandled exception (IRQn = {})", irqn); 99 | } 100 | } 101 | 102 | /// Possible interrupt priorities of the stm32f7. 103 | /// 104 | /// Lower number means higher priority: 105 | /// `P1` has a higher priority than e.g. `P2`, `P5`, ... 106 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 107 | #[repr(u8)] 108 | pub enum Priority { 109 | /// Priority 0 110 | P0 = 0, 111 | /// Priority 1 112 | P1, 113 | /// Priority 2 114 | P2, 115 | /// Priority 3 116 | P3, 117 | /// Priority 4 118 | P4, 119 | /// Priority 5 120 | P5, 121 | /// Priority 6 122 | P6, 123 | /// Priority 7 124 | P7, 125 | /// Priority 8 126 | P8, 127 | /// Priority 9 128 | P9, 129 | /// Priority 10 130 | P10, 131 | /// Priority 11 132 | P11, 133 | /// Priority 12 134 | P12, 135 | /// Priority 13 136 | P13, 137 | /// Priority 14 138 | P14, 139 | /// Priority 15 140 | P15, 141 | } 142 | struct PriorityDoesNotExistError(u8); 143 | 144 | impl Priority { 145 | /// Converts a u8 to a Priority. 146 | /// 147 | /// Returns an `Err` when no variant with the given `priority` exists. 148 | // use FromPrimitive? 149 | fn from_u8(priority: u8) -> Result { 150 | use self::Priority::*; 151 | match priority { 152 | 0 => Ok(P0), 153 | 1 => Ok(P1), 154 | 2 => Ok(P2), 155 | 3 => Ok(P3), 156 | 4 => Ok(P4), 157 | 5 => Ok(P5), 158 | 6 => Ok(P6), 159 | 7 => Ok(P7), 160 | 8 => Ok(P8), 161 | 9 => Ok(P9), 162 | 10 => Ok(P10), 163 | 11 => Ok(P11), 164 | 12 => Ok(P12), 165 | 13 => Ok(P13), 166 | 14 => Ok(P14), 167 | 15 => Ok(P15), 168 | _ => Err(PriorityDoesNotExistError(priority)), 169 | } 170 | } 171 | } 172 | 173 | -------------------------------------------------------------------------------- /interrupture/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interrupture" 3 | version = "0.1.1" 4 | authors = ["Oliver Scherer "] 5 | edition = "2018" 6 | description = "a HAL trait for `crossbeam`-like interrupt handling" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/embed-rs/stm32f7-discovery" 9 | 10 | [dependencies] 11 | bare-metal = "0.2.3" 12 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | /* NOTE K = KiBi = 1024 bytes */ 4 | FLASH : ORIGIN = 0x08000000, LENGTH = 1024K 5 | RAM : ORIGIN = 0x20000000, LENGTH = 320K 6 | } 7 | 8 | /* This is where the call stack will be allocated. */ 9 | /* The stack is of the full descending type. */ 10 | /* You may want to use this variable to locate the call stack and static 11 | variables in different memory regions. Below is shown the default value */ 12 | /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ 13 | 14 | /* You can use this symbol to customize the location of the .text section */ 15 | /* If omitted the .text section will be placed right after the .vector_table 16 | section */ 17 | /* This is required only on microcontrollers that store some configuration right 18 | after the vector table */ 19 | /* _stext = ORIGIN(FLASH) + 0x400; */ 20 | 21 | /* Size of the heap (in bytes) */ 22 | /* _heap_size = 1024; */ 23 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2019-04-26 2 | -------------------------------------------------------------------------------- /src/bin/polling.rs: -------------------------------------------------------------------------------- 1 | #![feature(alloc_error_handler)] 2 | #![no_main] 3 | #![no_std] 4 | 5 | #[macro_use] 6 | extern crate alloc; 7 | #[macro_use] 8 | extern crate stm32f7; 9 | #[macro_use] 10 | extern crate stm32f7_discovery; 11 | 12 | use alloc::vec::Vec; 13 | use alloc_cortex_m::CortexMHeap; 14 | use core::alloc::Layout as AllocLayout; 15 | use core::fmt::Write; 16 | use core::panic::PanicInfo; 17 | use cortex_m::{asm, interrupt, peripheral::NVIC}; 18 | use cortex_m_rt::{entry, exception, ExceptionFrame}; 19 | use cortex_m_semihosting::hio::{self, HStdout}; 20 | use smoltcp::{ 21 | dhcp::Dhcpv4Client, 22 | socket::{ 23 | Socket, SocketSet, TcpSocket, TcpSocketBuffer, 24 | UdpPacketMetadata, UdpSocket, UdpSocketBuffer, 25 | }, 26 | storage::{PacketBuffer, PacketMetadata}, 27 | time::Instant, 28 | wire::{EthernetAddress, IpCidr, IpEndpoint, Ipv4Address}, 29 | }; 30 | use stm32f7::stm32f7x6::{CorePeripherals, Interrupt, Peripherals}; 31 | use stm32f7_discovery::{ 32 | ethernet, 33 | gpio::{GpioPort, InputPin, OutputPin}, 34 | init, 35 | lcd::AudioWriter, 36 | lcd::{self, Color}, 37 | random::Rng, 38 | sd, 39 | system_clock::{self, Hz}, 40 | touch, 41 | }; 42 | 43 | #[global_allocator] 44 | static ALLOCATOR: CortexMHeap = CortexMHeap::empty(); 45 | 46 | const HEAP_SIZE: usize = 50 * 1024; // in bytes 47 | const ETH_ADDR: EthernetAddress = EthernetAddress([0x00, 0x08, 0xdc, 0xab, 0xcd, 0xef]); 48 | 49 | #[entry] 50 | fn main() -> ! { 51 | let core_peripherals = CorePeripherals::take().unwrap(); 52 | let mut systick = core_peripherals.SYST; 53 | let mut nvic = core_peripherals.NVIC; 54 | 55 | let peripherals = Peripherals::take().unwrap(); 56 | let mut rcc = peripherals.RCC; 57 | let mut pwr = peripherals.PWR; 58 | let mut flash = peripherals.FLASH; 59 | let mut fmc = peripherals.FMC; 60 | let mut ltdc = peripherals.LTDC; 61 | let mut sai_2 = peripherals.SAI2; 62 | let mut rng = peripherals.RNG; 63 | let mut sdmmc = peripherals.SDMMC1; 64 | let mut syscfg = peripherals.SYSCFG; 65 | let mut ethernet_mac = peripherals.ETHERNET_MAC; 66 | let ethernet_dma = peripherals.ETHERNET_DMA; 67 | 68 | init::init_system_clock_216mhz(&mut rcc, &mut pwr, &mut flash); 69 | init::enable_gpio_ports(&mut rcc); 70 | 71 | let gpio_a = GpioPort::new(peripherals.GPIOA); 72 | let gpio_b = GpioPort::new(peripherals.GPIOB); 73 | let gpio_c = GpioPort::new(peripherals.GPIOC); 74 | let gpio_d = GpioPort::new(peripherals.GPIOD); 75 | let gpio_e = GpioPort::new(peripherals.GPIOE); 76 | let gpio_f = GpioPort::new(peripherals.GPIOF); 77 | let gpio_g = GpioPort::new(peripherals.GPIOG); 78 | let gpio_h = GpioPort::new(peripherals.GPIOH); 79 | let gpio_i = GpioPort::new(peripherals.GPIOI); 80 | let gpio_j = GpioPort::new(peripherals.GPIOJ); 81 | let gpio_k = GpioPort::new(peripherals.GPIOK); 82 | let mut pins = init::pins( 83 | gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, gpio_f, gpio_g, gpio_h, gpio_i, gpio_j, gpio_k, 84 | ); 85 | 86 | // configures the system timer to trigger a SysTick exception every second 87 | init::init_systick(Hz(100), &mut systick, &rcc); 88 | systick.enable_interrupt(); 89 | 90 | init::init_sdram(&mut rcc, &mut fmc); 91 | let mut lcd = init::init_lcd(&mut ltdc, &mut rcc); 92 | pins.display_enable.set(true); 93 | pins.backlight.set(true); 94 | 95 | let mut layer_1 = lcd.layer_1().unwrap(); 96 | let mut layer_2 = lcd.layer_2().unwrap(); 97 | 98 | layer_1.clear(); 99 | layer_2.clear(); 100 | lcd::init_stdout(layer_2); 101 | 102 | println!("Hello World"); 103 | 104 | // Initialize the allocator BEFORE you use it 105 | unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } 106 | 107 | let _xs = vec![1, 2, 3]; 108 | 109 | let mut i2c_3 = init::init_i2c_3(peripherals.I2C3, &mut rcc); 110 | i2c_3.test_1(); 111 | i2c_3.test_2(); 112 | 113 | nvic.enable(Interrupt::EXTI0); 114 | 115 | let mut sd = sd::Sd::new(&mut sdmmc, &mut rcc, &pins.sdcard_present); 116 | 117 | init::init_sai_2(&mut sai_2, &mut rcc); 118 | init::init_wm8994(&mut i2c_3).expect("WM8994 init failed"); 119 | // touch initialization should be done after audio initialization, because the touch 120 | // controller might not be ready yet 121 | touch::check_family_id(&mut i2c_3).unwrap(); 122 | 123 | let mut rng = Rng::init(&mut rng, &mut rcc).expect("RNG init failed"); 124 | print!("Random numbers: "); 125 | for _ in 0..4 { 126 | print!( 127 | "{} ", 128 | rng.poll_and_get() 129 | .expect("Failed to generate random number") 130 | ); 131 | } 132 | println!(); 133 | 134 | // ethernet 135 | let mut ethernet_interface = ethernet::EthernetDevice::new( 136 | Default::default(), 137 | Default::default(), 138 | &mut rcc, 139 | &mut syscfg, 140 | &mut ethernet_mac, 141 | ethernet_dma, 142 | ETH_ADDR, 143 | ) 144 | .map(|device| { 145 | let iface = device.into_interface(Ipv4Address::new(192, 168, 42, 69)); 146 | let prev_ip_addr = iface.ipv4_addr().unwrap(); 147 | (iface, prev_ip_addr) 148 | }); 149 | if let Err(e) = ethernet_interface { 150 | println!("ethernet init failed: {:?}", e); 151 | }; 152 | 153 | let mut sockets = SocketSet::new(Vec::new()); 154 | let dhcp_rx_buffer = PacketBuffer::new([PacketMetadata::EMPTY; 1], vec![0; 1500]); 155 | let dhcp_tx_buffer = PacketBuffer::new([PacketMetadata::EMPTY; 1], vec![0; 3000]); 156 | let mut dhcp = Dhcpv4Client::new( 157 | &mut sockets, 158 | dhcp_rx_buffer, 159 | dhcp_tx_buffer, 160 | Instant::from_millis(system_clock::ms() as i64), 161 | ); 162 | 163 | let mut previous_button_state = pins.button.get(); 164 | let mut audio_writer = AudioWriter::new(); 165 | loop { 166 | // poll button state 167 | let current_button_state = pins.button.get(); 168 | if current_button_state != previous_button_state { 169 | if current_button_state { 170 | pins.led.toggle(); 171 | 172 | // trigger the `EXTI0` interrupt 173 | NVIC::pend(Interrupt::EXTI0); 174 | } 175 | 176 | previous_button_state = current_button_state; 177 | } 178 | 179 | // poll for new touch data 180 | for touch in &touch::touches(&mut i2c_3).unwrap() { 181 | layer_1.print_point_color_at( 182 | touch.x as usize, 183 | touch.y as usize, 184 | Color::from_hex(0xffff00), 185 | ); 186 | } 187 | 188 | // poll for new audio data 189 | while sai_2.bsr.read().freq().bit_is_clear() {} // fifo_request_flag 190 | let data0 = sai_2.bdr.read().data().bits(); 191 | while sai_2.bsr.read().freq().bit_is_clear() {} // fifo_request_flag 192 | let data1 = sai_2.bdr.read().data().bits(); 193 | 194 | audio_writer.set_next_col(&mut layer_1, data0, data1); 195 | 196 | // handle new ethernet packets 197 | if let Ok((ref mut iface, ref mut prev_ip_addr)) = ethernet_interface { 198 | let timestamp = Instant::from_millis(system_clock::ms() as i64); 199 | match iface.poll(&mut sockets, timestamp) { 200 | Err(::smoltcp::Error::Exhausted) => { 201 | continue; 202 | } 203 | Err(::smoltcp::Error::Unrecognized) => print!("U"), 204 | Err(e) => println!("Network error: {:?}", e), 205 | Ok(socket_changed) => { 206 | if socket_changed { 207 | for mut socket in sockets.iter_mut() { 208 | poll_socket(&mut socket).expect("socket poll failed"); 209 | } 210 | } 211 | } 212 | } 213 | 214 | let config = dhcp.poll(iface, &mut sockets, timestamp) 215 | .unwrap_or_else(|e| { println!("DHCP: {:?}", e); None}); 216 | let ip_addr = iface.ipv4_addr().unwrap(); 217 | if ip_addr != *prev_ip_addr { 218 | println!("\nAssigned a new IPv4 address: {}", ip_addr); 219 | iface.routes_mut().update(|routes_map| { 220 | routes_map 221 | .get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) 222 | .map(|default_route| { 223 | println!("Default gateway: {}", default_route.via_router); 224 | }); 225 | }); 226 | for dns_server in config.iter().flat_map(|c| c.dns_servers.iter()).filter_map(|x| x.as_ref()) { 227 | println!("DNS servers: {}", dns_server); 228 | } 229 | 230 | // TODO delete old sockets 231 | 232 | // add new sockets 233 | let endpoint = IpEndpoint::new(ip_addr.into(), 15); 234 | 235 | let udp_rx_buffer = 236 | UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 3], vec![0u8; 256]); 237 | let udp_tx_buffer = 238 | UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![0u8; 128]); 239 | let mut example_udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); 240 | example_udp_socket.bind(endpoint).unwrap(); 241 | sockets.add(example_udp_socket); 242 | 243 | let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; ethernet::MTU]); 244 | let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; ethernet::MTU]); 245 | let mut example_tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); 246 | example_tcp_socket.listen(endpoint).unwrap(); 247 | sockets.add(example_tcp_socket); 248 | 249 | *prev_ip_addr = ip_addr; 250 | } 251 | let mut timeout = dhcp.next_poll(timestamp); 252 | iface 253 | .poll_delay(&sockets, timestamp) 254 | .map(|sockets_timeout| timeout = sockets_timeout); 255 | // TODO await next interrupt 256 | } 257 | 258 | // Initialize the SD Card on insert and deinitialize on extract. 259 | if sd.card_present() && !sd.card_initialized() { 260 | if let Some(i_err) = sd::init(&mut sd).err() { 261 | println!("{:?}", i_err); 262 | } 263 | } else if !sd.card_present() && sd.card_initialized() { 264 | sd::de_init(&mut sd); 265 | } 266 | } 267 | } 268 | 269 | fn poll_socket(socket: &mut Socket) -> Result<(), smoltcp::Error> { 270 | match socket { 271 | &mut Socket::Udp(ref mut socket) => match socket.endpoint().port { 272 | 15 => loop { 273 | let reply; 274 | match socket.recv() { 275 | Ok((data, remote_endpoint)) => { 276 | let mut data = Vec::from(data); 277 | let len = data.len() - 1; 278 | data[..len].reverse(); 279 | reply = (data, remote_endpoint); 280 | } 281 | Err(smoltcp::Error::Exhausted) => break, 282 | Err(err) => return Err(err), 283 | } 284 | socket.send_slice(&reply.0, reply.1)?; 285 | }, 286 | _ => {} 287 | }, 288 | &mut Socket::Tcp(ref mut socket) => match socket.local_endpoint().port { 289 | 15 => { 290 | if !socket.may_recv() { 291 | return Ok(()); 292 | } 293 | let reply = socket.recv(|data| { 294 | if data.len() > 0 { 295 | let mut reply = Vec::from("tcp: "); 296 | let start_index = reply.len(); 297 | reply.extend_from_slice(data); 298 | reply[start_index..(start_index + data.len() - 1)].reverse(); 299 | (data.len(), Some(reply)) 300 | } else { 301 | (data.len(), None) 302 | } 303 | })?; 304 | if let Some(reply) = reply { 305 | assert_eq!(socket.send_slice(&reply)?, reply.len()); 306 | } 307 | } 308 | _ => {} 309 | }, 310 | _ => {} 311 | } 312 | Ok(()) 313 | } 314 | 315 | interrupt!(EXTI0, exti0, state: Option = None); 316 | 317 | fn exti0(_state: &mut Option) { 318 | println!("Interrupt fired! This means that the button was pressed."); 319 | } 320 | 321 | #[exception] 322 | fn SysTick() { 323 | system_clock::tick(); 324 | // print a `.` every 500ms 325 | if system_clock::ticks() % 50 == 0 && lcd::stdout::is_initialized() { 326 | print!("."); 327 | } 328 | } 329 | 330 | #[exception] 331 | fn HardFault(ef: &ExceptionFrame) -> ! { 332 | panic!("HardFault at {:#?}", ef); 333 | } 334 | 335 | // define what happens in an Out Of Memory (OOM) condition 336 | #[alloc_error_handler] 337 | fn rust_oom(_: AllocLayout) -> ! { 338 | panic!("out of memory"); 339 | } 340 | 341 | #[panic_handler] 342 | fn panic(info: &PanicInfo) -> ! { 343 | interrupt::disable(); 344 | 345 | if lcd::stdout::is_initialized() { 346 | println!("{}", info); 347 | } 348 | 349 | if let Ok(mut hstdout) = hio::hstdout() { 350 | let _ = writeln!(hstdout, "{}", info); 351 | } 352 | 353 | // OK to fire a breakpoint here because we know the microcontroller is connected to a debugger 354 | asm::bkpt(); 355 | 356 | loop {} 357 | } 358 | -------------------------------------------------------------------------------- /src/ethernet/init.rs: -------------------------------------------------------------------------------- 1 | pub use phy::Error as PhyError; 2 | 3 | use super::phy; 4 | use crate::system_clock; 5 | use stm32f7::stm32f7x6::{ETHERNET_DMA, ETHERNET_MAC, RCC, SYSCFG}; 6 | 7 | pub fn init( 8 | rcc: &mut RCC, 9 | syscfg: &mut SYSCFG, 10 | ethernet_mac: &mut ETHERNET_MAC, 11 | ethernet_dma: &mut ETHERNET_DMA, 12 | ) -> Result<(), PhyError> { 13 | // TODO delay after writes? 14 | 15 | // enable syscfg clock 16 | rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit()); 17 | // delay 18 | let _unused = rcc.apb2enr.read(); 19 | 20 | // TODO enable interrupt 21 | 22 | // enable ethernet clocks 23 | rcc.ahb1enr.modify(|_, w| { 24 | w.ethmacen().set_bit(); // ethernet mac clock enable 25 | w.ethmactxen().set_bit(); // ethernet mac transmission clock enable 26 | w.ethmacrxen().set_bit(); // ethernet mac reception clock enable 27 | w 28 | }); 29 | 30 | // select MII or RMII mode 31 | syscfg.pmc.modify(|_, w| w.mii_rmii_sel().set_bit()); // false = MII, true = RMII 32 | 33 | // ethernet software reset in DMA bus mode register 34 | ethernet_dma.dmabmr.modify(|_, w| w.sr().set_bit()); // set software reset bit 35 | while ethernet_dma.dmabmr.read().sr().bit_is_set() {} // wait for auto clear 36 | 37 | // MAC init: set clock range in MAC MII address register 38 | match system_clock::system_clock_speed() { 39 | f if f.0 >= 150_000_000 => { 40 | ethernet_mac.macmiiar.modify(|_, w| w.cr().cr_150_168()); // 150-168 MHz HCLK/102 41 | } 42 | _ => panic!("unsupported"), 43 | }; 44 | 45 | // init PHY 46 | let auto_neg_result = phy::init(ethernet_mac)?; 47 | assert!(auto_neg_result.duplex); 48 | assert_eq!(auto_neg_result.speed, phy::Speed::Speed100M); 49 | 50 | // MAC config 51 | // configuration register 52 | ethernet_mac.maccr.modify(|_, w| { 53 | // fast ethernet speed (false = 10Mbit/s, true = 100Mbit/s) 54 | match auto_neg_result.speed { 55 | phy::Speed::Speed100M => w.fes().fes100(), 56 | phy::Speed::Speed10M => w.fes().fes10(), 57 | }; 58 | // duplex mode 59 | if auto_neg_result.duplex { 60 | w.dm().full_duplex(); 61 | } else { 62 | w.dm().half_duplex(); 63 | } 64 | 65 | w.lm().normal(); // loopback mode 66 | w.apcs().strip(); // automatic pad/CRC stripping (only if length <= 1500 bytes) 67 | w.cstf().enabled(); // CRC stripping for Type frames 68 | w.ifg().ifg96(); // inter frame gap 96bit 69 | w.csd().disabled(); // carrier sense disable 70 | 71 | // When set, this bit enables IPv4 checksum checking for received frame payloads' 72 | // TCP/UDP/ICMP headers. When this bit is reset, the checksum offload function in the 73 | // receiver is disabled. 74 | w.ipco().disabled(); // IPv4 checksum offload 75 | 76 | // When this bit is set, the MAC disables the watchdog timer on the receiver, and can 77 | // receive frames of up to 16 384 bytes. When this bit is reset, the MAC allows no more 78 | // than 2 048 bytes of the frame being received and cuts off any bytes received after that. 79 | w.wd().enabled(); // watchdog enabled 80 | 81 | // When this bit is set, the MAC disables the jabber timer on the transmitter, and can 82 | // transfer frames of up to 16 384 bytes. When this bit is reset, the MAC cuts off the 83 | // transmitter if the application sends out more than 2 048 bytes of data during 84 | // transmission. 85 | w.jd().enabled(); // jabber enabled 86 | 87 | w 88 | }); 89 | 90 | // frame filter register 91 | ethernet_mac.macffr.modify(|_, w| { 92 | w.pm().disabled(); // Promiscuous mode 93 | w.ra().disabled(); // receive all (ignoring address filters) 94 | w.hpf().hash_only(); // Hash or perfect filter 95 | w.saf().disabled(); // Source address filter 96 | w.saif().normal(); // Source address inverse filtering 97 | w.daif().normal(); // Destination address inverse filtering 98 | w.bfd().enabled(); // broadcast frames enable 99 | w.ram().disabled(); // pass all multicast 100 | w.hu().perfect(); // hash unicast 101 | w.hm().perfect(); // hash multicast 102 | w.pcf().prevent_all(); // pass control frames 103 | w 104 | }); 105 | 106 | // hash table low/high register 107 | ethernet_mac.machtlr.modify(|_, w| w.htl().bits(0)); 108 | ethernet_mac.machthr.modify(|_, w| w.hth().bits(0)); 109 | 110 | // flow control register 111 | ethernet_mac.macfcr.modify(|_, w| { 112 | w.pt().bits(0); // pause time 113 | w.zqpd().set_bit(); // zero-quanta post disable bit 114 | w.plt().plt4(); // pause low threshold (plt4 == "Pause time minus 4 slot times") 115 | w.upfd().disabled(); // unicast pause frame detect 116 | w.rfce().disabled(); // receive flow control enable 117 | w.tfce().disabled(); // transmit flow control enable 118 | w.fcb().disable_back_pressure(); // flow control busy/back pressure activate 119 | 120 | w 121 | }); 122 | 123 | // VLAN tag register 124 | ethernet_mac.macvlantr.modify(|_, w| { 125 | w.vlanti().bits(0); // VLAN tag identifier (for receive frames) 126 | w.vlantc().vlantc16(); // 12-bit VLAN tag comparison (false == 16 bit comparison) 127 | 128 | w 129 | }); 130 | 131 | // DMA init 132 | // operation mode register 133 | ethernet_dma.dmaomr.modify(|_, w| { 134 | w.sr().stopped(); // start/stop receive (false = stopped) 135 | w.osf().set_bit(); // operate on second frame 136 | w.rtc().rtc64(); // receive threshold control (rtc64 = 64 bytes) 137 | w.fugf().drop(); // forward undersized good frames 138 | w.fef().drop(); // forward error frames 139 | w.st().stopped(); // start/stop transmission (false = stopped) 140 | w.ttc().ttc64(); // transmit threshold control (ttc64 = 64 bytes) 141 | w.ftf().clear_bit(); // flush transmit FIFO 142 | w.tsf().store_forward(); // transmit store and forward 143 | w.dfrf().clear_bit(); // disable flushing of received frames 144 | w.rsf().store_forward(); // receive store and forward 145 | w.dtcefd().enabled(); // dropping of TCP/IP checksum error frames disable 146 | 147 | w 148 | }); 149 | 150 | // bus mode register 151 | ethernet_dma.dmabmr.modify(|_, w| { 152 | w.aab().aligned(); // address-aligned beats 153 | w.fb().fixed(); // fixed burst 154 | w.rdp().rdp32(); // Rx DMA Programmable burst length 155 | w.pbl().pbl32(); // TX DMA Programmable burst length 156 | w.edfe().disabled(); // Enhanced descriptor format enable 157 | w.dsl().bits(0); // Descriptor skip length 158 | w.da().round_robin(); // DMA Arbitration (false = Round-robin with Rx:Tx priority given in `pm`) 159 | w.usp().separate(); // Use separate PBL 160 | 161 | w 162 | }); 163 | 164 | // interrupt enable register 165 | ethernet_dma.dmaier.modify(|_, w| { 166 | w.nise().set_bit(); // Normal interrupt summary enable 167 | w.rie().set_bit(); // Receive interrupt enable 168 | w 169 | }); 170 | 171 | // Initialize MAC address in ethernet MAC 172 | ethernet_mac.maca0hr.modify(|_, w| { 173 | #[allow(clippy::eq_op)] 174 | w.maca0h().bits(0 << 8 | 0) // high register 175 | }); 176 | ethernet_mac.maca0lr.modify(|_, w| { 177 | #[allow(clippy::eq_op)] 178 | w.maca0l().bits(0 << 24 | 0 << 16 | 0 << 8 | 2) // low register 179 | }); 180 | 181 | Ok(()) 182 | } 183 | 184 | pub fn start(ethernet_mac: &mut ETHERNET_MAC, ethernet_dma: &mut ETHERNET_DMA) { 185 | // enable MAC transmission and reception 186 | ethernet_mac.maccr.modify(|_, w| { 187 | w.te().set_bit(); 188 | w.re().set_bit(); 189 | w 190 | }); 191 | 192 | // flush transmit FIFO and enable DMA transmission/reception 193 | ethernet_dma.dmaomr.modify(|_, w| { 194 | w.ftf().set_bit(); 195 | w.st().set_bit(); 196 | w.sr().set_bit(); 197 | w 198 | }); 199 | } 200 | -------------------------------------------------------------------------------- /src/ethernet/phy.rs: -------------------------------------------------------------------------------- 1 | use crate::system_clock; 2 | use bit_field::BitField; 3 | use stm32f7::stm32f7x6::ETHERNET_MAC; 4 | 5 | const LAN8742A_PHY_ADDRESS: u8 = 0; 6 | 7 | const BASIC_CONTROL_REG: u8 = 0; 8 | const BASIC_STATUS_REG: u8 = 1; // basic status register 9 | const SPECIAL_STATUS_REG: u8 = 31; // special status register 10 | 11 | const PHY_RESET: u16 = 1 << 15; 12 | const AUTONEGOTIATION_ENABLE: u16 = 1 << 12; 13 | const AUTONEGOTIATION_RESTART: u16 = 1 << 9; 14 | 15 | const TIMEOUT_MS: usize = 5000; 16 | 17 | /// Errors that can happen during initialization of the PHY. 18 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 19 | pub enum Error { 20 | /// Timeout while waiting for ethernet link. 21 | LinkTimeout, 22 | /// Timeout while waiting for auto negotiation. 23 | AutoNegotiationTimeout, 24 | } 25 | 26 | pub struct AutoNegotiationResult { 27 | pub duplex: bool, 28 | pub speed: Speed, 29 | } 30 | 31 | #[derive(Debug, PartialEq, Eq)] 32 | pub enum Speed { 33 | Speed10M, 34 | Speed100M, 35 | } 36 | 37 | pub fn init(ethernet_mac: &mut ETHERNET_MAC) -> Result { 38 | // reset PHY 39 | phy_write( 40 | ethernet_mac, 41 | LAN8742A_PHY_ADDRESS, 42 | BASIC_CONTROL_REG, 43 | PHY_RESET, 44 | ); 45 | // wait 0.5s 46 | system_clock::wait_ms(500); 47 | // wait for reset bit auto clear 48 | while phy_read(ethernet_mac, LAN8742A_PHY_ADDRESS, BASIC_CONTROL_REG) & PHY_RESET != 0 {} 49 | 50 | // wait for link bit 51 | let timeout_ticks = system_clock::ms_to_ticks(TIMEOUT_MS); 52 | let ticks = system_clock::ticks(); 53 | while !phy_read(ethernet_mac, LAN8742A_PHY_ADDRESS, BASIC_STATUS_REG).get_bit(2) { 54 | if system_clock::ticks() - ticks > timeout_ticks { 55 | return Err(Error::LinkTimeout); // timeout 56 | } 57 | } 58 | 59 | // enable auto-negotiation 60 | phy_write( 61 | ethernet_mac, 62 | LAN8742A_PHY_ADDRESS, 63 | BASIC_CONTROL_REG, 64 | AUTONEGOTIATION_ENABLE | AUTONEGOTIATION_RESTART, 65 | ); 66 | 67 | // wait until auto-negotiation complete bit is set 68 | let ticks = system_clock::ticks(); 69 | while !phy_read(ethernet_mac, LAN8742A_PHY_ADDRESS, BASIC_STATUS_REG).get_bit(5) { 70 | if system_clock::ticks() - ticks > timeout_ticks { 71 | return Err(Error::AutoNegotiationTimeout); // timeout 72 | } 73 | } 74 | 75 | let ssr = phy_read(ethernet_mac, LAN8742A_PHY_ADDRESS, SPECIAL_STATUS_REG); 76 | // auto-negotiation done bit should be set 77 | assert!(ssr.get_bit(12)); 78 | let (duplex, speed) = match ssr.get_bits(2..5) { 79 | 0b001 => (false, Speed::Speed10M), // 10BASE-T half-duplex 80 | 0b101 => (true, Speed::Speed10M), // 10BASE-T full-duplex 81 | 0b010 => (false, Speed::Speed100M), // 100BASE-TX half-duplex 82 | 0b110 => (true, Speed::Speed100M), // 100BASE-TX full-duplex 83 | other => unreachable!("invalid auto-negotiation value: {:#b}", other), 84 | }; 85 | Ok(AutoNegotiationResult { 86 | duplex, 87 | speed, 88 | }) 89 | } 90 | 91 | fn phy_read(ethernet_mac: &mut ETHERNET_MAC, phy_address: u8, register: u8) -> u16 { 92 | // set the MII address register 93 | ethernet_mac.macmiiar.modify(|r, w| { 94 | assert!(!r.mb().is_busy()); // assert that MII is not busy 95 | 96 | w.pa().bits(phy_address); // set phy address 97 | w.mr().bits(register); // set mii register address 98 | w.mw().read(); // MII write operation (false = read) 99 | w.mb().busy(); // MII busy 100 | w 101 | }); 102 | 103 | // wait for completion (busy flag cleared) 104 | while ethernet_mac.macmiiar.read().mb().is_busy() {} 105 | 106 | // read the value from the MII data register 107 | ethernet_mac.macmiidr.read().md().bits() 108 | } 109 | 110 | fn phy_write(ethernet_mac: &mut ETHERNET_MAC, phy_address: u8, register: u8, value: u16) { 111 | assert!(!ethernet_mac.macmiiar.read().mb().is_busy()); // assert that MII is not busy 112 | 113 | // give the value to the MII data register 114 | ethernet_mac.macmiidr.write(|w| w.md().bits(value)); 115 | 116 | // set the MII address register 117 | ethernet_mac.macmiiar.modify(|_, w| { 118 | w.pa().bits(phy_address); // set phy address 119 | w.mr().bits(register); // set mii register address 120 | w.mw().write(); // MII write operation (true = write) 121 | w.mb().busy(); // MII busy 122 | w 123 | }); 124 | 125 | // wait for completion (busy flag cleared) 126 | while ethernet_mac.macmiiar.read().mb().is_busy() {} 127 | } 128 | -------------------------------------------------------------------------------- /src/ethernet/rx.rs: -------------------------------------------------------------------------------- 1 | use bit_field::BitField; 2 | use volatile::Volatile; 3 | 4 | #[derive(Debug, Clone, Copy)] 5 | #[repr(C)] 6 | pub struct RxDescriptor { 7 | word_0: u32, 8 | word_1: u32, 9 | word_2: u32, 10 | word_3: u32, 11 | } 12 | 13 | impl RxDescriptor { 14 | pub const fn empty() -> RxDescriptor { 15 | RxDescriptor { 16 | word_0: 0, 17 | word_1: 0, 18 | word_2: 0, 19 | word_3: 0, 20 | } 21 | } 22 | 23 | pub fn new(buffer_start: *const u8, buffer_size: usize) -> RxDescriptor { 24 | let mut descriptor = RxDescriptor::empty(); 25 | descriptor.set_buffer_1(buffer_start, buffer_size); 26 | descriptor.set_own(true); 27 | 28 | descriptor 29 | } 30 | 31 | pub fn reset(&mut self) { 32 | self.word_0 = 0; 33 | self.set_own(true); 34 | } 35 | 36 | #[allow(dead_code)] 37 | pub fn set_next(&mut self, next: *const Volatile) { 38 | assert_eq!(next as usize as u32 as usize as *const Volatile, next); 39 | self.word_3 = next as usize as u32; 40 | self.word_1 |= 1 << 14; // RCH: Second address chained 41 | } 42 | 43 | pub fn set_end_of_ring(&mut self, value: bool) { 44 | self.word_1.set_bit(15, value); 45 | } 46 | 47 | pub fn own(&self) -> bool { 48 | self.word_0.get_bit(31) 49 | } 50 | 51 | fn set_own(&mut self, value: bool) { 52 | self.word_0.set_bit(31, value); 53 | } 54 | 55 | fn set_buffer_1(&mut self, buffer_start: *const u8, buffer_size: usize) { 56 | assert_eq!(self.buffer_1_address(), 0); 57 | self.set_buffer_1_address(buffer_start as usize); 58 | self.set_buffer_1_size(buffer_size); 59 | } 60 | 61 | fn buffer_1_address(&self) -> usize { 62 | assert_eq!(self.word_2 as usize as u32, self.word_2); 63 | self.word_2 as usize 64 | } 65 | 66 | fn set_buffer_1_address(&mut self, buffer_address: usize) { 67 | assert_eq!(buffer_address as u32 as usize, buffer_address); 68 | self.word_2 = buffer_address as u32; 69 | } 70 | 71 | fn set_buffer_1_size(&mut self, size: usize) { 72 | assert_eq!(size as u32 as usize, size); 73 | let size = size as u32; 74 | self.word_1.set_bits(0..13, size); 75 | } 76 | 77 | pub fn frame_len(&self) -> usize { 78 | let val = self.word_0.get_bits(16..30); 79 | assert_eq!(val as usize as u32, val); 80 | val as usize 81 | } 82 | 83 | pub fn is_last_descriptor(&self) -> bool { 84 | self.word_0.get_bit(8) 85 | } 86 | 87 | pub fn is_first_descriptor(&self) -> bool { 88 | self.word_0.get_bit(9) 89 | } 90 | 91 | pub fn error(&self) -> bool { 92 | self.word_0.get_bit(15) 93 | } 94 | 95 | pub fn crc_error(&self) -> bool { 96 | self.word_0.get_bit(1) 97 | } 98 | 99 | pub fn receive_error(&self) -> bool { 100 | self.word_0.get_bit(3) 101 | } 102 | 103 | pub fn watchdog_timeout_error(&self) -> bool { 104 | self.word_0.get_bit(4) 105 | } 106 | 107 | pub fn late_collision_error(&self) -> bool { 108 | self.word_0.get_bit(6) 109 | } 110 | 111 | pub fn giant_frame_error(&self) -> bool { 112 | self.word_0.get_bit(7) 113 | } 114 | 115 | pub fn overflow_error(&self) -> bool { 116 | self.word_0.get_bit(11) 117 | } 118 | 119 | pub fn descriptor_error(&self) -> bool { 120 | self.word_0.get_bit(14) 121 | } 122 | 123 | pub fn checksum_result(&self) -> ChecksumResult { 124 | let w = self.word_0; 125 | match (w.get_bit(5), w.get_bit(7), w.get_bit(0)) { 126 | (false, false, false) => ChecksumResult::NovellRaw, 127 | (true, false, false) => ChecksumResult::Passed(false, false), 128 | (true, false, true) => ChecksumResult::Error(false, true), 129 | (true, true, false) => ChecksumResult::Error(true, false), 130 | (true, true, true) => ChecksumResult::Error(true, true), 131 | (false, false, true) => ChecksumResult::Passed(false, true), 132 | (false, true, true) => ChecksumResult::Passed(true, true), 133 | (false, true, false) => unreachable!(), 134 | } 135 | } 136 | } 137 | 138 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 139 | pub enum ChecksumResult { 140 | Passed(bool, bool), 141 | Error(bool, bool), 142 | NovellRaw, 143 | } 144 | -------------------------------------------------------------------------------- /src/ethernet/tx.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use bit_field::BitField; 3 | use core::{mem, slice}; 4 | 5 | #[derive(Debug, Clone, Copy)] 6 | #[repr(C)] 7 | pub struct TxDescriptor { 8 | word_0: u32, 9 | word_1: u32, 10 | word_2: u32, 11 | word_3: u32, 12 | } 13 | 14 | impl TxDescriptor { 15 | pub const fn empty() -> TxDescriptor { 16 | TxDescriptor { 17 | word_0: 0, 18 | word_1: 0, 19 | word_2: 0, 20 | word_3: 0, 21 | } 22 | } 23 | 24 | pub fn set_end_of_ring(&mut self, value: bool) { 25 | self.word_0.set_bit(21, value); 26 | } 27 | 28 | pub fn set_data(&mut self, data: Box<[u8]>) { 29 | assert!(!self.own(), "descriptor is still owned by the hardware"); 30 | 31 | mem::drop(self.buffer()); // drop old buffer if not already dropped 32 | 33 | self.set_buffer(data); 34 | self.set_first_segment(true); 35 | self.set_last_segment(true); 36 | self.set_own(true); 37 | } 38 | 39 | pub fn own(&self) -> bool { 40 | self.word_0.get_bit(31) 41 | } 42 | 43 | pub fn buffer(&mut self) -> Option> { 44 | assert!(!self.own(), "descriptor is still owned by the hardware"); 45 | match self.buffer_1_address() { 46 | 0 => None, 47 | ptr => { 48 | self.set_buffer_1_address(0); 49 | Some(unsafe { 50 | Box::from_raw(slice::from_raw_parts_mut( 51 | ptr as *mut u8, 52 | self.buffer_1_size(), 53 | )) 54 | }) 55 | } 56 | } 57 | } 58 | 59 | fn set_own(&mut self, value: bool) { 60 | self.word_0.set_bit(31, value); 61 | } 62 | 63 | fn set_first_segment(&mut self, value: bool) { 64 | self.word_0.set_bit(28, value); 65 | } 66 | 67 | fn set_last_segment(&mut self, value: bool) { 68 | self.word_0.set_bit(29, value); 69 | } 70 | 71 | fn set_buffer(&mut self, buffer: Box<[u8]>) { 72 | assert_eq!(self.buffer_1_address(), 0); 73 | self.set_buffer_1_address(buffer.as_ptr() as usize); 74 | self.set_buffer_1_size(buffer.len()); 75 | mem::forget(buffer); 76 | } 77 | 78 | fn buffer_1_address(&self) -> usize { 79 | assert_eq!(self.word_2 as usize as u32, self.word_2); 80 | self.word_2 as usize 81 | } 82 | 83 | fn set_buffer_1_address(&mut self, buffer_address: usize) { 84 | assert_eq!(buffer_address as u32 as usize, buffer_address); 85 | self.word_2 = buffer_address as u32; 86 | } 87 | 88 | fn buffer_1_size(&self) -> usize { 89 | let val = self.word_1.get_bits(0..13); 90 | assert_eq!(val as usize as u32, val); 91 | val as usize 92 | } 93 | 94 | fn set_buffer_1_size(&mut self, size: usize) { 95 | assert_eq!(size as u32 as usize, size); 96 | self.word_1.set_bits(0..13, size as u32); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/future_mutex.rs: -------------------------------------------------------------------------------- 1 | //! Provides a non-blocking Mutex based on Futures. 2 | 3 | use crate::mpsc_queue::{PopResult, Queue}; 4 | use core::task::{Waker, Context}; 5 | use core::{future::Future, mem, pin::Pin}; 6 | use futures::task::Poll; 7 | use spin::Mutex; 8 | 9 | /// A Mutex that yields instead of blocking. 10 | pub struct FutureMutex { 11 | mutex: Mutex, 12 | waker_queue: Queue, 13 | } 14 | 15 | impl FutureMutex { 16 | /// Creates a new Mutex wrapping the given data. 17 | pub fn new(user_data: T) -> Self { 18 | FutureMutex { 19 | mutex: Mutex::new(user_data), 20 | waker_queue: Queue::new(), 21 | } 22 | } 23 | } 24 | 25 | impl FutureMutex { 26 | /// Lock the mutex and execute the passed closure on the data. 27 | pub fn with<'a, R, F>(&'a self, f: F) -> impl Future + 'a 28 | where 29 | F: FnOnce(&mut T) -> R + Unpin + 'a, 30 | R: 'a, 31 | { 32 | FutureMutexResult { 33 | mutex: &self.mutex, 34 | f: Some(f), 35 | waker_queue: &self.waker_queue, 36 | } 37 | } 38 | } 39 | 40 | #[must_use = "futures do nothing unless polled"] 41 | struct FutureMutexResult<'a, T, R, F> 42 | where 43 | F: FnOnce(&mut T) -> R, 44 | { 45 | mutex: &'a Mutex, 46 | f: Option, 47 | waker_queue: &'a Queue, 48 | } 49 | 50 | impl<'a, T, R, F> Future for FutureMutexResult<'a, T, R, F> 51 | where 52 | F: FnOnce(&mut T) -> R + Unpin, 53 | { 54 | type Output = R; 55 | 56 | fn poll(mut self: Pin<&mut Self>, lw: &mut Context) -> Poll { 57 | match self.mutex.try_lock() { 58 | None => { 59 | self.waker_queue.push(lw.waker().clone()); 60 | Poll::Pending 61 | } 62 | Some(mut guard) => { 63 | let f = self.f.take().unwrap(); 64 | let ret = f(&mut guard); 65 | loop { 66 | match self.waker_queue.pop() { 67 | PopResult::Data(waker) => { 68 | waker.wake(); 69 | } 70 | PopResult::Empty => break, 71 | PopResult::Inconsistent => panic!("woken_tasks queue is inconsistent"), 72 | } 73 | } 74 | mem::drop(guard); 75 | Poll::Ready(ret) 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/gpio/mod.rs: -------------------------------------------------------------------------------- 1 | //! Abstractions for GPIO ports. 2 | 3 | use core::marker::PhantomData; 4 | 5 | pub use self::port::*; 6 | pub use self::traits::*; 7 | 8 | mod port; 9 | mod traits; 10 | 11 | /// The different possible modes of a GPIO pin. 12 | #[derive(Debug, Clone, Copy)] 13 | pub enum Mode { 14 | /// Use the pin for receiving data. 15 | Input, 16 | /// Use the pin for sending data. 17 | Output, 18 | /// Activate an alternate function of the pin to make it usable to some connected device. 19 | Alternate, 20 | /// Use the pin in analog mode. 21 | Analog, 22 | } 23 | 24 | /// Pull the pin value up or down. 25 | #[derive(Debug, Clone, Copy)] 26 | pub enum Resistor { 27 | /// Don't pull the value. 28 | NoPull, 29 | /// Pull the value to 1 if no data is sent/received. 30 | PullUp, 31 | /// Pull the value to 0 if no data is sent/received. 32 | PullDown, 33 | } 34 | 35 | /// The output mode of the pin. 36 | #[derive(Debug, Clone, Copy)] 37 | pub enum OutputType { 38 | /// Use push-pull mode. 39 | PushPull, 40 | /// Use open drain mode. 41 | OpenDrain, 42 | } 43 | 44 | /// The different output speeds. 45 | #[allow(missing_docs)] 46 | #[derive(Debug, Clone, Copy)] 47 | pub enum OutputSpeed { 48 | Low, 49 | Medium, 50 | High, 51 | VeryHigh, 52 | } 53 | 54 | /// The possible alternate functions. 55 | /// 56 | /// The alternate function number that a device uses is specified in the manual. 57 | #[allow(missing_docs)] 58 | #[derive(Debug, Clone, Copy)] 59 | pub enum AlternateFunction { 60 | AF0, 61 | AF1, 62 | AF2, 63 | AF3, 64 | AF4, 65 | AF5, 66 | AF6, 67 | AF7, 68 | AF8, 69 | AF9, 70 | AF10, 71 | AF11, 72 | AF12, 73 | AF13, 74 | AF14, 75 | AF15, 76 | } 77 | 78 | /// The 16 possible pin numbers. 79 | #[allow(missing_docs)] 80 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 81 | #[repr(u8)] 82 | pub enum PinNumber { 83 | Pin0 = 0, 84 | Pin1, 85 | Pin2, 86 | Pin3, 87 | Pin4, 88 | Pin5, 89 | Pin6, 90 | Pin7, 91 | Pin8, 92 | Pin9, 93 | Pin10, 94 | Pin11, 95 | Pin12, 96 | Pin13, 97 | Pin14, 98 | Pin15, 99 | } 100 | 101 | /// High level abstraction of a GPIO pin configured as input. 102 | pub trait InputPin { 103 | /// Get the current input value of the pin. 104 | fn get(&self) -> bool; 105 | } 106 | 107 | /// An implementation of the `InputPin` trait for the IDR abstractions of this module. 108 | pub struct InputPinImpl<'a, IDR: IdrTrait + 'a> { 109 | pin: PinNumber, 110 | input_data: ReadOnlyIdr<'a, IDR>, 111 | } 112 | 113 | impl<'a, IDR> InputPin for InputPinImpl<'a, IDR> 114 | where 115 | IDR: IdrTrait, 116 | { 117 | fn get(&self) -> bool { 118 | let value = self.input_data.read(); 119 | value.get(self.pin) 120 | } 121 | } 122 | 123 | struct ReadOnlyIdr<'a, IDR: IdrTrait>(&'a IDR); 124 | 125 | impl<'a, IDR: IdrTrait> ReadOnlyIdr<'a, IDR> { 126 | fn read(&self) -> IDR::R { 127 | self.0.read() 128 | } 129 | } 130 | 131 | unsafe impl<'a, IDR: IdrTrait> Sync for ReadOnlyIdr<'a, IDR> {} 132 | unsafe impl<'a, IDR: IdrTrait> Send for ReadOnlyIdr<'a, IDR> {} 133 | 134 | /// High level abstraction of a GPIO pin configured as output. 135 | pub trait OutputPin { 136 | /// Get the current output value of the pin. 137 | fn get(&self) -> bool; 138 | 139 | /// Set the output value of the pin. 140 | fn set(&mut self, value: bool); 141 | 142 | /// Toggle the output value of the pin. 143 | fn toggle(&mut self) { 144 | let current = self.get(); 145 | self.set(!current); 146 | } 147 | } 148 | 149 | /// An implementation of the `OutputPin` trait for the ODR and BSRR abstractions of this module. 150 | pub struct OutputPinImpl<'a, ODR: OdrTrait + 'a, BSRR: BsrrTrait + 'a> { 151 | pin: PinNumber, 152 | output_data: ReadOnlyOdr<'a, ODR>, 153 | bit_set_reset: BsrrRef<'a, BSRR>, 154 | } 155 | 156 | impl<'a, ODR, BSRR> OutputPin for OutputPinImpl<'a, ODR, BSRR> 157 | where 158 | ODR: OdrTrait, 159 | BSRR: BsrrTrait, 160 | { 161 | fn get(&self) -> bool { 162 | let value = self.output_data.read(); 163 | value.get(self.pin) 164 | } 165 | 166 | fn set(&mut self, value: bool) { 167 | self.bit_set_reset.set(self.pin, value); 168 | } 169 | } 170 | 171 | struct ReadOnlyOdr<'a, ODR: OdrTrait>(&'a ODR); 172 | 173 | impl<'a, ODR: OdrTrait> ReadOnlyOdr<'a, ODR> { 174 | fn read(&self) -> ODR::R { 175 | self.0.read() 176 | } 177 | } 178 | 179 | unsafe impl<'a, ODR: OdrTrait> Send for ReadOnlyOdr<'a, ODR> {} 180 | unsafe impl<'a, ODR: OdrTrait> Sync for ReadOnlyOdr<'a, ODR> {} 181 | 182 | #[derive(Debug, Clone)] 183 | struct BsrrRef<'a, BSRR: 'a> { 184 | register: *mut BSRR, 185 | phantom: PhantomData<&'a BSRR>, 186 | } 187 | 188 | unsafe impl<'a, U> Send for BsrrRef<'a, U> {} 189 | 190 | impl<'a, BSRR> BsrrRef<'a, BSRR> 191 | where 192 | BSRR: BsrrTrait, 193 | { 194 | fn set(&self, pin: PinNumber, value: bool) { 195 | unsafe { (&mut *self.register) }.write(|w| if value { w.set(pin) } else { w.reset(pin) }); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/gpio/traits.rs: -------------------------------------------------------------------------------- 1 | use super::PinNumber; 2 | use stm32f7::stm32f7x6::{gpioa, gpiob, gpiod}; 3 | 4 | /// Abstracts over the three different types of input data registers (IDRs). 5 | /// 6 | /// The GPIO ports A and B have separate IDR types because they use slightly different 7 | /// reset values. This trait allows to create generic functions that work with all three 8 | /// IDR types. 9 | pub trait IdrTrait { 10 | /// Represents an IDR value. 11 | type R: IdrR; 12 | 13 | /// Read the current value from the IDR register. 14 | fn read(&self) -> Self::R; 15 | } 16 | 17 | /// Represents an IDR value that specifies the input value of all pins of the port. 18 | pub trait IdrR { 19 | /// Returns the input value of the specified pin. 20 | fn get(&self, pin: PinNumber) -> bool; 21 | } 22 | 23 | /// Abstracts over the three different types of output data registers (ODRs). 24 | /// 25 | /// The GPIO ports A and B have separate ODR types because they use slightly different 26 | /// reset values. This trait allows to create generic functions that work with all three 27 | /// ODR types. 28 | pub trait OdrTrait { 29 | /// Represents an ODR value. 30 | type R: OdrR; 31 | 32 | /// Read the current value from the ODR register. 33 | fn read(&self) -> Self::R; 34 | } 35 | 36 | /// Represents an ODR value that specifies the output value of all pins of the port. 37 | pub trait OdrR { 38 | /// Returns the output value of the specified pin. 39 | fn get(&self, pin: PinNumber) -> bool; 40 | } 41 | 42 | /// Abstracts over the three different types of bit set and reset registers (BSRRs). 43 | /// 44 | /// The GPIO ports A and B have separate BSRR types because they use slightly different 45 | /// reset values. This trait allows to create generic functions that work with all three 46 | /// BSRR types. 47 | pub trait BsrrTrait { 48 | /// A type that allows BSRR write operations. 49 | type W: BsrrW; 50 | 51 | /// Perform write operations on the BSRR register. 52 | fn write(&mut self, f: F) 53 | where 54 | F: FnOnce(&mut Self::W) -> &mut Self::W; 55 | } 56 | 57 | /// Allows writing the BSRR register. 58 | /// 59 | /// Bits that are neither `set` or `reset` keep their previous value. 60 | pub trait BsrrW { 61 | /// Set the output value of the specified pin to 1. 62 | fn set(&mut self, pin: PinNumber) -> &mut Self; 63 | 64 | /// Set the output value of the specified pin to 0. 65 | fn reset(&mut self, pin: PinNumber) -> &mut Self; 66 | } 67 | 68 | macro_rules! impl_traits_for { 69 | ($gpio:tt) => { 70 | impl IdrTrait for $gpio::IDR { 71 | type R = $gpio::idr::R; 72 | 73 | fn read(&self) -> Self::R { 74 | $gpio::IDR::read(self) 75 | } 76 | } 77 | 78 | impl IdrR for $gpio::idr::R { 79 | fn get(&self, pin: PinNumber) -> bool { 80 | use super::PinNumber::*; 81 | match pin { 82 | Pin0 => self.idr0().bit_is_set(), 83 | Pin1 => self.idr1().bit_is_set(), 84 | Pin2 => self.idr2().bit_is_set(), 85 | Pin3 => self.idr3().bit_is_set(), 86 | Pin4 => self.idr4().bit_is_set(), 87 | Pin5 => self.idr5().bit_is_set(), 88 | Pin6 => self.idr6().bit_is_set(), 89 | Pin7 => self.idr7().bit_is_set(), 90 | Pin8 => self.idr8().bit_is_set(), 91 | Pin9 => self.idr9().bit_is_set(), 92 | Pin10 => self.idr10().bit_is_set(), 93 | Pin11 => self.idr11().bit_is_set(), 94 | Pin12 => self.idr12().bit_is_set(), 95 | Pin13 => self.idr13().bit_is_set(), 96 | Pin14 => self.idr14().bit_is_set(), 97 | Pin15 => self.idr15().bit_is_set(), 98 | } 99 | } 100 | } 101 | 102 | impl OdrTrait for $gpio::ODR { 103 | type R = $gpio::odr::R; 104 | 105 | fn read(&self) -> Self::R { 106 | $gpio::ODR::read(self) 107 | } 108 | } 109 | 110 | impl OdrR for $gpio::odr::R { 111 | fn get(&self, pin: PinNumber) -> bool { 112 | use super::PinNumber::*; 113 | match pin { 114 | Pin0 => self.odr0().bit_is_set(), 115 | Pin1 => self.odr1().bit_is_set(), 116 | Pin2 => self.odr2().bit_is_set(), 117 | Pin3 => self.odr3().bit_is_set(), 118 | Pin4 => self.odr4().bit_is_set(), 119 | Pin5 => self.odr5().bit_is_set(), 120 | Pin6 => self.odr6().bit_is_set(), 121 | Pin7 => self.odr7().bit_is_set(), 122 | Pin8 => self.odr8().bit_is_set(), 123 | Pin9 => self.odr9().bit_is_set(), 124 | Pin10 => self.odr10().bit_is_set(), 125 | Pin11 => self.odr11().bit_is_set(), 126 | Pin12 => self.odr12().bit_is_set(), 127 | Pin13 => self.odr13().bit_is_set(), 128 | Pin14 => self.odr14().bit_is_set(), 129 | Pin15 => self.odr15().bit_is_set(), 130 | } 131 | } 132 | } 133 | 134 | impl BsrrTrait for $gpio::BSRR { 135 | type W = $gpio::bsrr::W; 136 | 137 | fn write(&mut self, f: F) 138 | where 139 | F: FnOnce(&mut Self::W) -> &mut Self::W, 140 | { 141 | $gpio::BSRR::write(self, f) 142 | } 143 | } 144 | 145 | impl BsrrW for $gpio::bsrr::W { 146 | fn set(&mut self, pin: PinNumber) -> &mut Self { 147 | use super::PinNumber::*; 148 | match pin { 149 | Pin0 => self.bs0().set(), 150 | Pin1 => self.bs1().set(), 151 | Pin2 => self.bs2().set(), 152 | Pin3 => self.bs3().set(), 153 | Pin4 => self.bs4().set(), 154 | Pin5 => self.bs5().set(), 155 | Pin6 => self.bs6().set(), 156 | Pin7 => self.bs7().set(), 157 | Pin8 => self.bs8().set(), 158 | Pin9 => self.bs9().set(), 159 | Pin10 => self.bs10().set(), 160 | Pin11 => self.bs11().set(), 161 | Pin12 => self.bs12().set(), 162 | Pin13 => self.bs13().set(), 163 | Pin14 => self.bs14().set(), 164 | Pin15 => self.bs15().set(), 165 | } 166 | } 167 | 168 | fn reset(&mut self, pin: PinNumber) -> &mut Self { 169 | use super::PinNumber::*; 170 | match pin { 171 | Pin0 => self.br0().reset(), 172 | Pin1 => self.br1().reset(), 173 | Pin2 => self.br2().reset(), 174 | Pin3 => self.br3().reset(), 175 | Pin4 => self.br4().reset(), 176 | Pin5 => self.br5().reset(), 177 | Pin6 => self.br6().reset(), 178 | Pin7 => self.br7().reset(), 179 | Pin8 => self.br8().reset(), 180 | Pin9 => self.br9().reset(), 181 | Pin10 => self.br10().reset(), 182 | Pin11 => self.br11().reset(), 183 | Pin12 => self.br12().reset(), 184 | Pin13 => self.br13().reset(), 185 | Pin14 => self.br14().reset(), 186 | Pin15 => self.br15().reset(), 187 | } 188 | } 189 | } 190 | }; 191 | } 192 | 193 | impl_traits_for!(gpioa); 194 | impl_traits_for!(gpiob); 195 | impl_traits_for!(gpiod); 196 | -------------------------------------------------------------------------------- /src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Safe abstractions for an I2C bus. 2 | 3 | use core::iter::TrustedLen; 4 | use core::marker::PhantomData; 5 | use core::ops::Deref; 6 | use embedded_hal; 7 | use stm32f7::stm32f7x6::{self as device, i2c1, RCC}; 8 | 9 | /// This trait marks all valid I2C types. Used to provide generic interfaces. 10 | /// 11 | /// TODO: replace by trait alias when they're fully implemented 12 | pub trait I2cTrait: Deref {} 13 | 14 | impl I2cTrait for device::I2C1 {} 15 | impl I2cTrait for device::I2C2 {} 16 | impl I2cTrait for device::I2C3 {} 17 | 18 | /// Represents an I2C (Inter-Integrated Circuit) bus. 19 | pub struct I2C(I); 20 | 21 | /// Errors that can happen while accessing the I2C bus. 22 | #[derive(Debug)] 23 | pub enum Error { 24 | /// A NACK flag (negative acknowledgement) was detected. 25 | Nack, 26 | } 27 | 28 | /// An I2C address. 29 | /// 30 | /// Currently only 7 bit addresses are supported. 31 | #[derive(Debug, Clone, Copy)] 32 | pub struct Address(u16); 33 | 34 | impl Address { 35 | /// Create a 7 bit I2C address. 36 | pub const fn bits_7(addr: u8) -> Address { 37 | Address((addr as u16) << 1) 38 | } 39 | } 40 | 41 | fn icr_clear_all(w: &mut i2c1::icr::W) -> &mut i2c1::icr::W { 42 | w.alertcf().set_bit(); // alert clear flag 43 | w.timoutcf().set_bit(); // timeout detection clear flag 44 | w.peccf().set_bit(); // PEC error clear flag 45 | w.ovrcf().set_bit(); // overrun/underrun clear flag 46 | w.arlocf().set_bit(); // arbitration loss clear flag 47 | w.berrcf().set_bit(); // bus error clear flag 48 | w.stopcf().set_bit(); // stop detection clear flag 49 | w.nackcf().set_bit(); // not acknowledge clear flag 50 | w.addrcf().set_bit(); // address matched clear flag 51 | w 52 | } 53 | 54 | /// An active connection to a device on the I2C bus. 55 | /// 56 | /// Allows reading and writing the registers of the device. 57 | pub struct I2cConnection<'a, I: I2cTrait, T: RegisterType> { 58 | i2c: &'a mut I2C, 59 | device_address: Address, 60 | register_type: PhantomData, 61 | } 62 | 63 | /// Valid register types of I2C devices. 64 | /// 65 | /// This trait is implemented for the `u8` and `u16` types. 66 | pub trait RegisterType: Sized { 67 | /// Convert the register type into a byte slice and pass it to the specified closure. 68 | fn write(&self, f: F) -> Result<(), Error> 69 | where 70 | F: FnOnce(&[u8]) -> Result<(), Error>; 71 | 72 | /// Call the specified closure with a mutable reference to a byte slice and then convert it 73 | /// to the register type. 74 | fn read(f: F) -> Result 75 | where 76 | F: FnOnce(&mut [u8]) -> Result<(), Error>; 77 | } 78 | 79 | impl RegisterType for u8 { 80 | fn write(&self, f: F) -> Result<(), Error> 81 | where 82 | F: FnOnce(&[u8]) -> Result<(), Error>, 83 | { 84 | f(&[*self]) 85 | } 86 | 87 | fn read(f: F) -> Result 88 | where 89 | F: FnOnce(&mut [u8]) -> Result<(), Error>, 90 | { 91 | let mut buf = [0]; 92 | f(&mut buf)?; 93 | Ok(buf[0]) 94 | } 95 | } 96 | 97 | impl RegisterType for u16 { 98 | fn write(&self, f: F) -> Result<(), Error> 99 | where 100 | F: FnOnce(&[u8]) -> Result<(), Error>, 101 | { 102 | f(&[(*self >> 8) as u8, *self as u8]) 103 | } 104 | 105 | fn read(f: F) -> Result 106 | where 107 | F: FnOnce(&mut [u8]) -> Result<(), Error>, 108 | { 109 | let mut buf = [0, 0]; 110 | f(&mut buf)?; 111 | Ok(u16::from(buf[0]) << 8 | u16::from(buf[1])) 112 | } 113 | } 114 | 115 | impl<'a, I: I2cTrait, T: RegisterType> I2cConnection<'a, I, T> { 116 | fn start(&mut self, read: bool, bytes: u8) { 117 | self.i2c.0.cr2.write(|w| { 118 | w.sadd().bits(self.device_address.0); // slave_address 119 | w.start().set_bit(); // start_generation 120 | w.rd_wrn().bit(read); // read_transfer 121 | w.nbytes().bits(bytes); // number_of_bytes 122 | w.autoend().clear_bit(); // automatic_end_mode 123 | w 124 | }) 125 | } 126 | 127 | fn write_bytes(&mut self, bytes: ITER) -> Result<(), Error> 128 | where 129 | ITER: Iterator + TrustedLen, 130 | { 131 | assert!(bytes.size_hint().1.is_some()); 132 | assert_eq!( 133 | bytes.size_hint().0 as u8 as usize, 134 | bytes.size_hint().0, 135 | "transfers > 255 bytes are not implemented yet" 136 | ); 137 | self.start(false, bytes.size_hint().0 as u8); 138 | 139 | for b in bytes { 140 | self.i2c.wait_for_txis()?; 141 | self.i2c.0.txdr.modify(|_, w| w.txdata().bits(b)); // transmit_data 142 | } 143 | 144 | self.i2c.wait_for_transfer_complete()?; 145 | 146 | self.clear_status_flags(); 147 | 148 | // reset cr2 149 | self.i2c.0.cr2.write(|w| w); 150 | 151 | Ok(()) 152 | } 153 | 154 | fn read_bytes_raw<'b, ITER>(&mut self, buffer: ITER) -> Result<(), Error> 155 | where 156 | ITER: Iterator + TrustedLen, 157 | { 158 | assert!(buffer.size_hint().1.is_some()); 159 | assert_eq!( 160 | buffer.size_hint().0 as u8 as usize, 161 | buffer.size_hint().0, 162 | "transfers > 255 bytes are not implemented yet" 163 | ); 164 | self.start(true, buffer.size_hint().0 as u8); 165 | 166 | // read data from receive data register 167 | for b in buffer { 168 | self.i2c.wait_for_rxne()?; 169 | *b = self.i2c.0.rxdr.read().rxdata().bits(); // receive_data 170 | } 171 | 172 | self.i2c.wait_for_transfer_complete()?; 173 | 174 | self.clear_status_flags(); 175 | 176 | // reset cr2 177 | self.i2c.0.cr2.write(|w| w); 178 | 179 | Ok(()) 180 | } 181 | 182 | fn pre(&mut self) { 183 | self.clear_status_flags(); 184 | // flush transmit data register 185 | self.i2c.0.isr.modify(|_, w| w.txe().set_bit()); // flush_txdr 186 | } 187 | 188 | fn clear_status_flags(&mut self) { 189 | self.i2c.0.icr.write(|w| icr_clear_all(w)); 190 | } 191 | 192 | /// Read the current value from the specified register. 193 | pub fn read(&mut self, register_address: T) -> Result { 194 | self.pre(); 195 | 196 | register_address.write(|addr_bytes| self.write_bytes(addr_bytes.iter().cloned()))?; 197 | 198 | T::read(|val_bytes| self.read_bytes_raw(val_bytes.iter_mut())) 199 | } 200 | 201 | /// Read bytes from the specified register into the specified buffer. 202 | pub fn read_bytes(&mut self, register_address: T, bytes: &mut [u8]) -> Result<(), Error> { 203 | self.pre(); 204 | 205 | register_address.write(|addr_bytes| self.write_bytes(addr_bytes.iter().cloned()))?; 206 | 207 | self.read_bytes_raw(bytes.iter_mut()) 208 | } 209 | 210 | /// Write the specified bytes into to specified register. 211 | pub fn write(&mut self, register_address: T, value: T) -> Result<(), Error> { 212 | self.pre(); 213 | register_address.write(|addr_bytes| { 214 | value.write(|val_bytes| { 215 | self.write_bytes(addr_bytes.iter().cloned().chain(val_bytes.iter().cloned())) 216 | }) 217 | }) 218 | } 219 | } 220 | 221 | impl I2C { 222 | /// Connects to the specified device and run the closure `f` with the connection as argument. 223 | /// 224 | /// This function takes an exclusive reference to the `I2C` type because it blocks the I2C 225 | /// bus. The connection is active until the closure `f` returns. 226 | pub fn connect(&mut self, device_address: Address, f: F) -> Result<(), Error> 227 | where 228 | T: RegisterType, 229 | F: FnOnce(I2cConnection) -> Result<(), Error>, 230 | { 231 | { 232 | let conn = I2cConnection { 233 | i2c: self, 234 | device_address, 235 | register_type: PhantomData, 236 | }; 237 | f(conn)?; 238 | } 239 | self.stop() 240 | } 241 | 242 | /// Stop the active connection by sending a stop symbol. 243 | pub fn stop(&mut self) -> Result<(), Error> { 244 | self.0.cr2.modify(|_, w| w.stop().set_bit()); 245 | 246 | // reset cr2 247 | self.0.cr2.write(|w| w); 248 | 249 | self.wait_for_stop() 250 | } 251 | 252 | /// Update a device register. 253 | pub fn update( 254 | &mut self, 255 | device_address: Address, 256 | register_address: u16, 257 | f: F, 258 | ) -> Result<(), Error> 259 | where 260 | F: FnOnce(&mut u16), 261 | { 262 | self.connect(device_address, |mut conn| { 263 | let mut value = conn.read(register_address)?; 264 | f(&mut value); 265 | conn.write(register_address, value) 266 | }) 267 | } 268 | 269 | /// Wait for “transmit interrupt status” flag 270 | fn wait_for_txis(&self) -> Result<(), Error> { 271 | loop { 272 | let isr = self.0.isr.read(); 273 | if isr.nackf().bit_is_set() { 274 | // nack_received 275 | return Err(Error::Nack); 276 | } 277 | if isr.txis().bit_is_set() { 278 | return Ok(()); 279 | } 280 | } 281 | } 282 | 283 | /// Wait for "receive data register not empty" flag 284 | fn wait_for_rxne(&self) -> Result<(), Error> { 285 | loop { 286 | let isr = self.0.isr.read(); 287 | if isr.nackf().bit_is_set() { 288 | // nack_received 289 | return Err(Error::Nack); 290 | } 291 | if isr.rxne().bit_is_set() { 292 | return Ok(()); 293 | } 294 | } 295 | } 296 | 297 | /// Wait for “transfer complete” flag 298 | fn wait_for_transfer_complete(&self) -> Result<(), Error> { 299 | loop { 300 | let isr = self.0.isr.read(); 301 | if isr.nackf().bit_is_set() { 302 | // nack_received 303 | return Err(Error::Nack); 304 | } 305 | if isr.tc().bit_is_set() { 306 | // transfer_complete 307 | return Ok(()); 308 | } 309 | } 310 | } 311 | 312 | /// Wait for automatically generated stop flag 313 | fn wait_for_stop(&self) -> Result<(), Error> { 314 | loop { 315 | let isr = self.0.isr.read(); 316 | if isr.nackf().bit_is_set() { 317 | // nack_received 318 | return Err(Error::Nack); 319 | } 320 | if isr.stopf().bit_is_set() { 321 | // stop_detected 322 | return Ok(()); 323 | } 324 | } 325 | } 326 | 327 | /// Provokes a NACK and checks if the response is as expected. Panics otherwise. 328 | pub fn test_1(&mut self) { 329 | let i2c = &mut self.0; 330 | 331 | i2c.cr2.modify(|_, w| { 332 | w.sadd().bits(Address::bits_7(0b101_0101).0); // slave_address 333 | w.start().set_bit(); // start_generation 334 | w.nbytes().bits(0); // number_of_bytes 335 | w.autoend().set_bit(); // automatic_end_mode 336 | w 337 | }); 338 | 339 | loop { 340 | let isr = i2c.isr.read(); 341 | if isr.nackf().bit_is_set() { 342 | // nack_received 343 | break; 344 | } 345 | assert!(isr.stopf().bit_is_clear()); // stop_detected 346 | } 347 | 348 | // clear status flags 349 | i2c.icr.write(|w| icr_clear_all(w)); 350 | } 351 | 352 | /// Try to access all I2C addresses. Panics on test failure. 353 | #[allow(dead_code)] 354 | pub fn test_2(&mut self) { 355 | let i2c = &mut self.0; 356 | 357 | let mut addr = 0; 358 | loop { 359 | i2c.cr2.modify(|_, w| { 360 | w.sadd().bits(Address::bits_7(addr).0); // slave_address 361 | w.start().set_bit(); // start_generation 362 | w.nbytes().bits(0); // number_of_bytes 363 | w.autoend().set_bit(); // automatic_end_mode 364 | w 365 | }); 366 | 367 | let mut isr = i2c.isr.read(); 368 | loop { 369 | if isr.nackf().bit_is_set() || isr.stopf().bit_is_set() { 370 | // nack_received or stop_detected 371 | break; 372 | } 373 | isr = i2c.isr.read(); 374 | } 375 | 376 | if !isr.nackf().bit_is_set() { 377 | let _x = addr; 378 | } else { 379 | while i2c.isr.read().busy().bit_is_set() {} 380 | // clear status flags 381 | i2c.icr.write(|w| icr_clear_all(w)); 382 | } 383 | 384 | addr += 1; 385 | if addr >= 0x80 { 386 | return; 387 | } 388 | } 389 | } 390 | } 391 | 392 | impl embedded_hal::blocking::i2c::Read for I2C { 393 | type Error = Error; 394 | 395 | fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { 396 | self.connect( 397 | Address::bits_7(address), 398 | |mut connection: I2cConnection| connection.read_bytes_raw(buffer.iter_mut()), 399 | ) 400 | } 401 | } 402 | 403 | impl embedded_hal::blocking::i2c::Write for I2C { 404 | type Error = Error; 405 | 406 | fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { 407 | self.connect( 408 | Address::bits_7(address), 409 | |mut connection: I2cConnection| connection.write_bytes(bytes.iter().cloned()), 410 | ) 411 | } 412 | } 413 | 414 | impl embedded_hal::blocking::i2c::WriteRead for I2C { 415 | type Error = Error; 416 | 417 | fn write_read( 418 | &mut self, 419 | address: u8, 420 | bytes: &[u8], 421 | buffer: &mut [u8], 422 | ) -> Result<(), Self::Error> { 423 | self.connect( 424 | Address::bits_7(address), 425 | |mut connection: I2cConnection| { 426 | connection.write_bytes(bytes.iter().cloned())?; 427 | connection.read_bytes_raw(buffer.iter_mut()) 428 | }, 429 | ) 430 | } 431 | } 432 | 433 | /// Initialize the I2C bus and return an `I2C` type. 434 | pub fn init(i2c: I, rcc: &mut RCC) -> I2C { 435 | // enable clocks 436 | rcc.apb1enr.modify(|_, w| w.i2c3en().enabled()); 437 | 438 | // disable I2C peripheral 439 | i2c.cr1.modify(|_, w| w.pe().clear_bit()); // peripheral_enable register 440 | 441 | // configure timing register TODO: check/understand values 442 | i2c.timingr.modify(|_, w| { 443 | w.presc().bits(0x4); // timing_prescaler 444 | w.scldel().bits(0x9); // data_setup_time 445 | w.sdadel().bits(0x1); // data_hold_time 446 | w.sclh().bits(0x27); // scl_high_period 447 | w.scll().bits(0x32); // scl_low_period 448 | w 449 | }); 450 | 451 | // configure oar1 452 | i2c.oar1.modify(|_, w| w.oa1en().clear_bit()); // own_address_1_enable register 453 | i2c.oar1.modify(|_, w| { 454 | w.oa1().bits(0x00); // own_address_1 455 | w.oa1mode().clear_bit(); // 10 bit mode 456 | w.oa1en().clear_bit(); // TODO 457 | w 458 | }); 459 | 460 | // configure cr2 461 | i2c.cr2.modify(|_, w| { 462 | w.add10().clear_bit(); // 10_bit_addressing mode 463 | w.autoend().clear_bit(); // automatic_end_mode 464 | w 465 | }); 466 | 467 | // configure oar2 468 | i2c.oar2.modify(|_, w| { 469 | w.oa2en().clear_bit() // own_address_2_enable 470 | }); 471 | 472 | // configure cr1 473 | i2c.cr1.modify(|_, w| { 474 | w.gcen().clear_bit(); // general_call 475 | w.nostretch().clear_bit(); // clock_stretching_disable 476 | w.pe().set_bit(); // peripheral_enable 477 | w 478 | }); 479 | // wait that init can finish 480 | crate::system_clock::wait_ms(50); 481 | 482 | I2C(i2c) 483 | } 484 | -------------------------------------------------------------------------------- /src/interrupts/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module implements the `InterruptController` trait of 2 | //! the `interrupture` crate in order to offer the high level 3 | //! interrupt interface on `stm32f7x6` 4 | 5 | pub use interrupture_stm32f7x6::*; 6 | 7 | pub mod primask_mutex; 8 | 9 | /// Wait for interrupt. 10 | /// 11 | /// This function calls the `wfi` assembler command of the cortex-m processors. 12 | pub unsafe fn wfi() { 13 | ::cortex_m::asm::wfi(); 14 | } 15 | -------------------------------------------------------------------------------- /src/interrupts/primask_mutex.rs: -------------------------------------------------------------------------------- 1 | //! Mutex for interrupt synchronization. 2 | //! 3 | //! The mutex uses the `primask` core register of the cortex m processor 4 | //! to disable interrupts and synchronize the access to shared variables. 5 | //! 6 | //! Since the access to the data is synchronized (no interrupt can preempt 7 | //! the current code in the critical section) the mutex implements `Send` and `Sync` when 8 | //! the synchronized data implements `Send`. 9 | 10 | use core::cell::UnsafeCell; 11 | use cortex_m::interrupt; 12 | use cortex_m::register::primask; 13 | 14 | /// Mutex that uses the `primask` core register from the cortem m processor to disable 15 | /// interrupts before the critical section and enables interrupts again, when interrupts 16 | /// were enabled before entering the critical section. 17 | /// 18 | /// Since the access to the data is synchronized (no interrupt can preempt 19 | /// the current code in the critical section) the mutex implements `Send` and `Sync` when 20 | /// the synchronized data implements `Send`. 21 | #[derive(Debug)] 22 | pub struct PrimaskMutex { 23 | data: UnsafeCell, 24 | } 25 | 26 | unsafe impl Send for PrimaskMutex {} 27 | unsafe impl Sync for PrimaskMutex {} 28 | 29 | impl PrimaskMutex { 30 | /// Takes ownership over the data and return a new mutex 31 | /// 32 | /// # Examples 33 | /// ``` 34 | /// let x = 5; 35 | /// let mutex = PrimaskMutex::new(x); 36 | /// ``` 37 | pub const fn new(data: T) -> PrimaskMutex { 38 | PrimaskMutex { 39 | data: UnsafeCell::new(data), 40 | } 41 | } 42 | 43 | /// Executes the closure `critical_section` without interrupts. 44 | /// 45 | /// If interrupts were enabled before entering the critical section, the interrupts are enabled 46 | /// again after the critical section 47 | /// 48 | /// # Examples 49 | /// ``` 50 | /// let x = 5; 51 | /// let mutex = PrimaskMutex::new(x); 52 | /// mutex.lock(|data| { 53 | /// // Interrupt free section 54 | /// // Safe, because 'atomic' block 55 | /// data += 5; 56 | /// }); 57 | /// // Interrupts are enabled again, if interrupts was enabled before the critical section 58 | /// ``` 59 | pub fn lock(&self, critical_section: F) -> R 60 | where 61 | F: FnOnce(&mut T) -> R, 62 | { 63 | // PRIMASK = 1 => Prevents the activation of all exceptions with configurable priority 64 | let primask_active = primask::read().is_active(); 65 | interrupt::disable(); 66 | 67 | let result = critical_section(unsafe { &mut *self.data.get() }); 68 | 69 | // If PRIMASK was active (Interrupts enabled) then enable interrupts again 70 | if primask_active { 71 | unsafe { interrupt::enable() }; 72 | } 73 | 74 | result 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/lcd/color.rs: -------------------------------------------------------------------------------- 1 | /// Represents a color with alpha, red, green, and blue channels. 2 | #[derive(Debug, Clone, Copy, PartialEq)] 3 | pub struct Color { 4 | /// The red channel. 5 | pub red: u8, 6 | /// The green channel. 7 | pub green: u8, 8 | /// The blue channel. 9 | pub blue: u8, 10 | /// The alpha channel (0 is transparent, 255 is opaque). 11 | pub alpha: u8, 12 | } 13 | 14 | impl Color { 15 | /// Creates a color from the passed RGB values. The alpha channel is set to 255 (opaque). 16 | pub const fn rgb(red: u8, green: u8, blue: u8) -> Color { 17 | Self::rgba(red, green, blue, 255) 18 | } 19 | 20 | /// Converts the color to RGB. The alpha channel is ignored. 21 | pub fn to_rgb(self) -> u32 { 22 | (u32::from(self.red) << 16) | (u32::from(self.green) << 8) | u32::from(self.blue) 23 | } 24 | 25 | /// Creates a color from the passed values. 26 | pub const fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color { 27 | Color { 28 | red, 29 | green, 30 | blue, 31 | alpha, 32 | } 33 | } 34 | 35 | /// Creates a color from the passed hex RGB value. The alpha channel is set to 255 (opaque). 36 | pub fn from_hex(color: u32) -> Color { 37 | assert_eq!(color >> (8 * 3), 0); 38 | Color { 39 | red: (color >> 16) as u8, 40 | green: (color >> 8) as u8, 41 | blue: color as u8, 42 | alpha: 255, 43 | } 44 | } 45 | 46 | /// Converts the color to RGB. The alpha channel is ignored. 47 | pub fn to_rgb888(self) -> u32 { 48 | self.to_rgb() 49 | } 50 | 51 | /// Creates a color from the passed RGB value. The alpha channel is set to 255 (opaque). 52 | pub fn from_rgb888(color: u32) -> Color { 53 | Color::from_hex(color) 54 | } 55 | 56 | /// Converts the color to ARGB. 57 | pub fn to_argb8888(self) -> u32 { 58 | (u32::from(self.alpha) << 24) | self.to_rgb888() 59 | } 60 | 61 | /// Creates a color from the passed ARGB value. 62 | pub const fn from_argb8888(color: u32) -> Color { 63 | Color { 64 | red: (color >> 16) as u8, 65 | green: (color >> 8) as u8, 66 | blue: color as u8, 67 | alpha: (color >> 24) as u8, 68 | } 69 | } 70 | 71 | /// Converts the color to ARGB1555. 72 | pub fn to_argb1555(self) -> u16 { 73 | (u16::from(self.alpha) & 0x80) << 8 74 | | (u16::from(self.red) & 0xf8) << 7 75 | | (u16::from(self.green) & 0xf8) << 2 76 | | (u16::from(self.blue) & 0xf8) >> 3 77 | } 78 | 79 | /// Creates a color from the passed ARGB1555 value. 80 | pub const fn from_argb1555(color: u16) -> Color { 81 | Color { 82 | alpha: ((color >> 8) & 0x80) as u8, 83 | red: ((color >> 7) & 0xf8) as u8, 84 | green: ((color >> 2) & 0xf8) as u8, 85 | blue: ((color << 3) & 0xf8) as u8, 86 | } 87 | } 88 | 89 | /// Creates a color from the passed HSV value. 90 | pub fn from_hsv(hue: i32, saturation: f32, value: f32) -> Color { 91 | let mut h = hue % 360; 92 | if h < 0 { 93 | h += 360; 94 | } 95 | 96 | let c = value * saturation; 97 | let x = (h as i32 % 120) as f32 / 60f32 - 1f32; 98 | let x = c * (1f32 - if x < 0f32 { -x } else { x }); 99 | let m = value - c; 100 | 101 | let mut rgb = (0f32, 0f32, 0f32); 102 | if h < 60 { 103 | rgb.0 = c; 104 | rgb.1 = x; 105 | } else if h < 120 { 106 | rgb.0 = x; 107 | rgb.1 = c; 108 | } else if h < 180 { 109 | rgb.1 = c; 110 | rgb.2 = x; 111 | } else if h < 240 { 112 | rgb.1 = x; 113 | rgb.2 = c; 114 | } else if h < 300 { 115 | rgb.0 = x; 116 | rgb.2 = c; 117 | } else { 118 | rgb.0 = c; 119 | rgb.2 = x; 120 | } 121 | 122 | rgb.0 += m; 123 | rgb.1 += m; 124 | rgb.2 += m; 125 | 126 | Color::rgb( 127 | (255f32 * rgb.0) as u8, 128 | (255f32 * rgb.1) as u8, 129 | (255f32 * rgb.2) as u8, 130 | ) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/lcd/font.rs: -------------------------------------------------------------------------------- 1 | use alloc::Vec; 2 | 3 | pub struct FontRenderer<'a> { 4 | font: Font<'a>, 5 | height: f32, 6 | } 7 | 8 | impl<'a> FontRenderer<'a> { 9 | pub fn new(font_data: &[u8], font_height: f32) -> FontRenderer { 10 | let collection = FontCollection::from_bytes(font_data); 11 | // only succeeds if collection consists of one font 12 | let font = collection.into_font().unwrap(); 13 | FontRenderer { 14 | font, 15 | height: font_height, 16 | } 17 | } 18 | 19 | pub fn font_height(&self) -> f32 { 20 | self.height 21 | } 22 | 23 | pub fn layout(&self, s: &str) -> Vec { 24 | let scale = Scale { 25 | x: self.height, 26 | y: self.height, 27 | }; 28 | 29 | // The origin of a line of text is at the baseline (roughly where non-descending letters 30 | // sit). We don't want to clip the text, so we shift it down with an offset when laying 31 | // it out. v_metrics.ascent is the distance between the baseline and the highest edge of 32 | // any glyph in the font. That's enough to guarantee that there's no clipping. 33 | let v_metrics = self.font.v_metrics(scale); 34 | let offset = point(0.0, v_metrics.ascent); 35 | 36 | self.font.layout(s, scale, offset).collect() 37 | } 38 | 39 | pub fn render(&self, s: &str, mut draw_pixel: F) -> usize 40 | where 41 | F: FnMut(usize, usize, f32), 42 | { 43 | let glyphs = self.layout(s); 44 | let pixel_height = self.height.ceil() as usize; 45 | 46 | // Find the most visually pleasing width to display 47 | let width = glyphs 48 | .iter() 49 | .rev() 50 | .map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width) 51 | .next() 52 | .unwrap_or(0.0) 53 | .ceil() as usize; 54 | 55 | for g in glyphs { 56 | if let Some(bb) = g.pixel_bounding_box() { 57 | g.draw(|x, y, mut v| { 58 | assert!(v >= 0.0 - 1.0e-5); 59 | assert!(v <= 1.0 + 1.0e-5); 60 | if v < 0.0 { 61 | v = 0.0; 62 | } 63 | if v > 1.0 { 64 | v = 1.0; 65 | } 66 | let x = x as i32 + bb.min.x; 67 | let y = y as i32 + bb.min.y; 68 | // There's still a possibility that the glyph clips the boundaries of the bitmap 69 | if x >= 0 && x < width as i32 && y >= 0 && y < pixel_height as i32 { 70 | draw_pixel(x as usize, y as usize, v); 71 | } 72 | }); 73 | } 74 | } 75 | return width; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/lcd/init.rs: -------------------------------------------------------------------------------- 1 | use super::Lcd; 2 | use stm32f7::stm32f7x6::{LTDC, RCC}; 3 | 4 | /// Initializes the LCD controller. 5 | /// 6 | /// The SDRAM must be initialized before this function is called. See the 7 | /// [`init_sdram`] function for more information. 8 | /// 9 | /// [`init_sdram`]: crate::init::init_sdram 10 | pub fn init<'a>(ltdc: &'a mut LTDC, rcc: &mut RCC) -> Lcd<'a> { 11 | use crate::lcd::{self, LAYER_1_START, LAYER_2_START}; 12 | const HEIGHT: u16 = lcd::HEIGHT as u16; 13 | const WIDTH: u16 = lcd::WIDTH as u16; 14 | const LAYER_1_OCTETS_PER_PIXEL: u16 = lcd::LAYER_1_OCTETS_PER_PIXEL as u16; 15 | const LAYER_2_OCTETS_PER_PIXEL: u16 = lcd::LAYER_2_OCTETS_PER_PIXEL as u16; 16 | 17 | // enable LTDC and DMA2D clocks 18 | rcc.ahb1enr.modify(|_, w| w.dma2den().enabled()); 19 | rcc.apb2enr.modify(|_, w| w.ltdcen().enabled()); 20 | 21 | // disable LTDC 22 | ltdc.gcr.modify(|_, w| w.ltdcen().clear_bit()); 23 | 24 | // disable PLLSAI clock 25 | rcc.cr.modify(|_, w| w.pllsaion().clear_bit()); 26 | while rcc.cr.read().pllsairdy().bit_is_set() {} 27 | 28 | rcc.pllsaicfgr.modify(|_, w| unsafe { 29 | w.pllsain().bits(192); 30 | w.pllsair().bits(5); 31 | w 32 | }); 33 | 34 | // set division factor for LCD_CLK 35 | rcc.dkcfgr1.modify(|_, w| unsafe { 36 | w.pllsaidivr().bits(0b01 /* = 4 */) 37 | }); 38 | 39 | // enable PLLSAI clock 40 | rcc.cr.modify(|_, w| w.pllsaion().set_bit()); 41 | while rcc.cr.read().pllsairdy().bit_is_clear() {} 42 | 43 | // configure the HS, VS, DE and PC polarity 44 | ltdc.gcr.modify(|_, w| { 45 | w.pcpol().bit(false); 46 | w.depol().bit(false); 47 | w.hspol().bit(false); 48 | w.vspol().bit(false); 49 | w 50 | }); 51 | 52 | // set synchronization size 53 | ltdc.sscr.modify(|_, w| unsafe { 54 | w.hsw().bits(41 - 1); // horizontal_sync_width 55 | w.vsh().bits(10 - 1); // vertical_sync_height 56 | w 57 | }); 58 | 59 | // set accumulated back porch 60 | ltdc.bpcr.modify(|_, w| unsafe { 61 | w.ahbp().bits(41 + 13 - 1); // accumulated_horizontal_back_porch 62 | w.avbp().bits(10 + 2 - 1); // accumulated_vertical_back_porch 63 | w 64 | }); 65 | 66 | // set accumulated active width 67 | ltdc.awcr.modify(|_, w| unsafe { 68 | w.aav().bits(WIDTH + 41 + 13 - 1); // accumulated_active_width 69 | w.aah().bits(HEIGHT + 10 + 2 - 1); // accumulated_active_height 70 | w 71 | }); 72 | 73 | // set total width 74 | ltdc.twcr.modify(|_, w| unsafe { 75 | w.totalw().bits(WIDTH + 41 + 13 + 32 - 1); // total_width 76 | w.totalh().bits(HEIGHT + 10 + 2 + 2 - 1); // total_height 77 | w 78 | }); 79 | 80 | // set background color 81 | ltdc.bccr.modify(|_, w| unsafe { w.bc().bits(0x00_00_ff) }); // background_color blue 82 | 83 | // enable the transfer error interrupt and the FIFO underrun interrupt 84 | ltdc.ier.modify(|_, w| { 85 | w.terrie().bit(true); // TRANSFER_ERROR_INTERRUPT_ENABLE 86 | w.fuie().bit(true); // FIFO_UNDERRUN_INTERRUPT_ENABLE 87 | w 88 | }); 89 | 90 | // enable LTDC 91 | ltdc.gcr.modify(|_, w| w.ltdcen().bit(true)); 92 | 93 | // configure layers 94 | 95 | // configure horizontal start and stop position 96 | ltdc.l1whpcr.modify(|_, w| unsafe { 97 | w.whstpos().bits(0 + 41 + 13); // window_horizontal_start_position 98 | w.whsppos().bits(WIDTH + 41 + 13 - 1); // window_horizontal_stop_position 99 | w 100 | }); 101 | ltdc.l2whpcr.modify(|_, w| unsafe { 102 | w.whstpos().bits(0 + 41 + 13); // window_horizontal_start_position 103 | w.whsppos().bits(WIDTH + 41 + 13 - 1); // window_horizontal_stop_position 104 | w 105 | }); 106 | 107 | // configure vertical start and stop position 108 | ltdc.l1wvpcr.modify(|_, w| unsafe { 109 | w.wvstpos().bits(0 + 10 + 2); // window_vertical_start_position 110 | w.wvsppos().bits(HEIGHT + 10 + 2 - 1); // window_vertical_stop_position 111 | w 112 | }); 113 | ltdc.l2wvpcr.modify(|_, w| unsafe { 114 | w.wvstpos().bits(0 + 10 + 2); // window_vertical_start_position 115 | w.wvsppos().bits(HEIGHT + 10 + 2 - 1); // window_vertical_stop_position 116 | w 117 | }); 118 | 119 | // specify pixed format 120 | ltdc.l1pfcr.modify(|_, w| unsafe { w.pf().bits(0b000) }); // set_pixel_format to ARGB8888 121 | ltdc.l2pfcr.modify(|_, w| unsafe { w.pf().bits(0b111) }); // set_pixel_format to AL88 122 | 123 | // configure default color values 124 | ltdc.l1dccr.modify(|_, w| unsafe { 125 | w.dcalpha().bits(0); 126 | w.dcred().bits(0); 127 | w.dcgreen().bits(0); 128 | w.dcblue().bits(0); 129 | w 130 | }); 131 | ltdc.l2dccr.modify(|_, w| unsafe { 132 | w.dcalpha().bits(0); 133 | w.dcred().bits(0); 134 | w.dcgreen().bits(0); 135 | w.dcblue().bits(0); 136 | w 137 | }); 138 | 139 | // specify constant alpha value 140 | ltdc.l1cacr.modify(|_, w| unsafe { w.consta().bits(255) }); // constant_alpha 141 | ltdc.l2cacr.modify(|_, w| unsafe { w.consta().bits(255) }); // constant_alpha 142 | 143 | // specify blending factors 144 | ltdc.l1bfcr.modify(|_, w| unsafe { 145 | w.bf1().bits(0b110); // set_blending_factor_1 to PixelAlphaTimesConstantAlpha 146 | w.bf2().bits(0b111); // set_blending_factor_2 to OneMinusPixelAlphaTimesConstantAlpha 147 | w 148 | }); 149 | ltdc.l2bfcr.modify(|_, w| unsafe { 150 | w.bf1().bits(0b110); // set_blending_factor_1 to PixelAlphaTimesConstantAlpha 151 | w.bf2().bits(0b111); // set_blending_factor_2 to OneMinusPixelAlphaTimesConstantAlpha 152 | w 153 | }); 154 | 155 | // configure color frame buffer start address 156 | ltdc.l1cfbar 157 | .modify(|_, w| unsafe { w.cfbadd().bits(LAYER_1_START as u32) }); 158 | ltdc.l2cfbar 159 | .modify(|_, w| unsafe { w.cfbadd().bits(LAYER_2_START as u32) }); 160 | 161 | // configure color frame buffer line length and pitch 162 | ltdc.l1cfblr.modify(|_, w| unsafe { 163 | w.cfbp().bits(WIDTH * LAYER_1_OCTETS_PER_PIXEL); // pitch 164 | w.cfbll().bits(WIDTH * LAYER_1_OCTETS_PER_PIXEL + 3); // line_length 165 | w 166 | }); 167 | ltdc.l2cfblr.modify(|_, w| unsafe { 168 | w.cfbp().bits(WIDTH * LAYER_2_OCTETS_PER_PIXEL); // pitch 169 | w.cfbll().bits(WIDTH * LAYER_2_OCTETS_PER_PIXEL + 3); // line_length 170 | w 171 | }); 172 | 173 | // configure frame buffer line number 174 | ltdc.l1cfblnr 175 | .modify(|_, w| unsafe { w.cfblnbr().bits(HEIGHT) }); // line_number 176 | ltdc.l2cfblnr 177 | .modify(|_, w| unsafe { w.cfblnbr().bits(HEIGHT) }); // line_number 178 | 179 | // enable layers 180 | ltdc.l1cr.modify(|_, w| w.len().set_bit()); 181 | ltdc.l2cr.modify(|_, w| w.len().set_bit().cluten().set_bit()); 182 | 183 | // reload shadow registers 184 | ltdc.srcr.modify(|_, w| w.imr().set_bit()); // IMMEDIATE_RELOAD 185 | 186 | let mut lcd = Lcd::new(ltdc); 187 | lcd.set_color_lookup_table(255, super::Color::rgb(255, 255, 255)); 188 | lcd 189 | } 190 | -------------------------------------------------------------------------------- /src/lcd/mod.rs: -------------------------------------------------------------------------------- 1 | //! Functions for accessing and writing text to the LCD. 2 | //! 3 | //! The display has two layers that are blended on top of each other, and a background layer 4 | //! with an uniform color. 5 | 6 | pub use self::color::Color; 7 | pub use self::init::init; 8 | pub use self::stdout::init as init_stdout; 9 | 10 | use core::{fmt, ptr}; 11 | use stm32f7::stm32f7x6::LTDC; 12 | 13 | #[macro_use] 14 | pub mod stdout; 15 | mod color; 16 | mod init; 17 | 18 | /// The height of the display in pixels. 19 | pub const HEIGHT: usize = 272; 20 | /// The width of the display in pixels. 21 | pub const WIDTH: usize = 480; 22 | 23 | /// The number of bytes per pixel for layer 1. 24 | pub const LAYER_1_OCTETS_PER_PIXEL: usize = 4; 25 | /// The length of the layer 1 buffer in bytes. 26 | pub const LAYER_1_LENGTH: usize = HEIGHT * WIDTH * LAYER_1_OCTETS_PER_PIXEL; 27 | /// The number of bytes per pixel for layer 2. 28 | pub const LAYER_2_OCTETS_PER_PIXEL: usize = 2; 29 | /// The length of the layer 1 buffer in bytes. 30 | pub const LAYER_2_LENGTH: usize = HEIGHT * WIDTH * LAYER_2_OCTETS_PER_PIXEL; 31 | 32 | /// Start address of the SDRAM where the framebuffers live. 33 | pub const SDRAM_START: usize = 0xC000_0000; 34 | /// Start address of the layer 1 framebuffer. 35 | pub const LAYER_1_START: usize = SDRAM_START; 36 | /// Start address of the layer 2 framebuffer. 37 | pub const LAYER_2_START: usize = SDRAM_START + LAYER_1_LENGTH; 38 | 39 | /// Represents the LCD and provides methods to access both layers. 40 | pub struct Lcd<'a> { 41 | controller: &'a mut LTDC, 42 | layer_1_in_use: bool, 43 | layer_2_in_use: bool, 44 | } 45 | 46 | impl<'a> Lcd<'a> { 47 | fn new(ltdc: &'a mut LTDC) -> Self { 48 | Self { 49 | controller: ltdc, 50 | layer_1_in_use: false, 51 | layer_2_in_use: false, 52 | } 53 | } 54 | 55 | /// Sets the color of the background layer. 56 | pub fn set_background_color(&mut self, color: Color) { 57 | self.controller 58 | .bccr 59 | .modify(|_, w| unsafe { w.bc().bits(color.to_rgb()) }); 60 | } 61 | 62 | /// Sets the color `i` in the lookup table for layer 2 63 | pub fn set_color_lookup_table(&mut self, i: u8, color: Color) { 64 | self.controller 65 | .l2clutwr 66 | .write(|w| unsafe { w 67 | .clutadd().bits(i) 68 | .red().bits(color.red) 69 | .green().bits(color.green) 70 | .blue().bits(color.blue) 71 | }); 72 | } 73 | 74 | /// Returns a reference to layer 1. 75 | pub fn layer_1(&mut self) -> Option> { 76 | if self.layer_1_in_use { 77 | None 78 | } else { 79 | Some(Layer { 80 | framebuffer: FramebufferArgb8888::new(LAYER_1_START), 81 | }) 82 | } 83 | } 84 | 85 | /// Returns a reference to layer 2. 86 | pub fn layer_2(&mut self) -> Option> { 87 | if self.layer_2_in_use { 88 | None 89 | } else { 90 | Some(Layer { 91 | framebuffer: FramebufferAl88::new(LAYER_2_START), 92 | }) 93 | } 94 | } 95 | } 96 | 97 | /// Represents a buffer of pixels. 98 | pub trait Framebuffer { 99 | /// Set the pixel at the specified coordinates to the specified color. 100 | fn set_pixel(&mut self, x: usize, y: usize, color: Color); 101 | } 102 | 103 | /// A framebuffer in the ARGB8888 format. 104 | /// 105 | /// It uses 8bits for alpha, red, green, and black respectively, totaling in 32bits per pixel. 106 | pub struct FramebufferArgb8888 { 107 | base_addr: usize, 108 | } 109 | 110 | impl FramebufferArgb8888 { 111 | const fn new(base_addr: usize) -> Self { 112 | Self { base_addr } 113 | } 114 | } 115 | 116 | impl Framebuffer for FramebufferArgb8888 { 117 | fn set_pixel(&mut self, x: usize, y: usize, color: Color) { 118 | let pixel = y * WIDTH + x; 119 | let pixel_ptr = (self.base_addr + pixel * LAYER_1_OCTETS_PER_PIXEL) as *mut u32; 120 | unsafe { ptr::write_volatile(pixel_ptr, color.to_argb8888()) }; 121 | } 122 | } 123 | 124 | /// A framebuffer in the AL88 format. 125 | /// 126 | /// There are 8bits for the alpha channel and 8 bits for specifying a color using a 127 | /// lookup table. Thus, each pixel is represented by 16bits. 128 | pub struct FramebufferAl88 { 129 | base_addr: usize, 130 | } 131 | 132 | impl FramebufferAl88 { 133 | const fn new(base_addr: usize) -> Self { 134 | Self { base_addr } 135 | } 136 | } 137 | 138 | impl Framebuffer for FramebufferAl88 { 139 | fn set_pixel(&mut self, x: usize, y: usize, color: Color) { 140 | let pixel = y * WIDTH + x; 141 | let pixel_ptr = (self.base_addr + pixel * LAYER_2_OCTETS_PER_PIXEL) as *mut u16; 142 | unsafe { ptr::write_volatile(pixel_ptr, u16::from(color.alpha) << 8 | u16::from(color.red)) }; 143 | } 144 | } 145 | 146 | /// Represents a layer of the LCD controller. 147 | pub struct Layer { 148 | framebuffer: T, 149 | } 150 | 151 | impl Layer { 152 | /// Fill the layer with horizontal stripes. 153 | /// 154 | /// Useful for testing. 155 | pub fn horizontal_stripes(&mut self) { 156 | let colors = [ 157 | 0xff_ff_ff, 0xcc_cc_cc, 0x99_99_99, 0x66_66_66, 0x33_33_33, 0x00_00_00, 0xff_00_00, 0x00_00_ff, 158 | ]; 159 | 160 | // horizontal stripes 161 | for i in 0..HEIGHT { 162 | for j in 0..WIDTH { 163 | self.framebuffer.set_pixel( 164 | j, 165 | i, 166 | Color::from_rgb888(colors[(i / 10) % colors.len()]), 167 | ); 168 | } 169 | } 170 | } 171 | 172 | /// Fill the layer with vertical stripes. 173 | /// 174 | /// Useful for testing. 175 | pub fn vertical_stripes(&mut self) { 176 | let colors = [ 177 | 0xcc_cc_cc, 0x99_99_99, 0x66_66_66, 0x33_33_33, 0x00_00_00, 0xff_00_00, 0x00_00_ff, 0xff_ff_ff, 178 | ]; 179 | 180 | // vertical stripes 181 | for i in 0..HEIGHT { 182 | for j in 0..WIDTH { 183 | self.framebuffer.set_pixel( 184 | j, 185 | i, 186 | Color::from_rgb888(colors[(j / 10) % colors.len()]), 187 | ); 188 | } 189 | } 190 | } 191 | 192 | /// Clear all pixels. 193 | /// 194 | /// This method sets each pixel to transparent or black, depending on the framebuffer format. 195 | pub fn clear(&mut self) { 196 | for i in 0..HEIGHT { 197 | for j in 0..WIDTH { 198 | self.framebuffer.set_pixel(j, i, Color::from_argb8888(0)); 199 | } 200 | } 201 | } 202 | 203 | /// Sets the pixel at the specified coordinates to white. 204 | pub fn print_point_at(&mut self, x: usize, y: usize) { 205 | self.print_point_color_at(x, y, Color::from_hex(0xff_ff_ff)); 206 | } 207 | 208 | /// Sets the pixel at the specified coordinates to the specified color. 209 | pub fn print_point_color_at(&mut self, x: usize, y: usize, color: Color) { 210 | assert!(x < WIDTH); 211 | assert!(y < HEIGHT); 212 | 213 | self.framebuffer.set_pixel(x, y, color); 214 | } 215 | 216 | /// Creates a text writer on this layer. 217 | pub fn text_writer(&mut self) -> TextWriter { 218 | TextWriter { 219 | layer: self, 220 | x_pos: 0, 221 | y_pos: 0, 222 | } 223 | } 224 | } 225 | 226 | /// Allows to print audio data. 227 | pub struct AudioWriter { 228 | next_pixel: usize, 229 | next_col: usize, 230 | prev_value: (usize, usize), 231 | } 232 | 233 | impl AudioWriter { 234 | /// Creates a new audio writer starting at the left edge of the screen. 235 | pub const fn new() -> Self { 236 | AudioWriter { 237 | next_pixel: 0, 238 | next_col: 0, 239 | prev_value: (0, 0), 240 | } 241 | } 242 | 243 | /// Sets the next pixel on the layer. 244 | /// 245 | /// Useful for testing. 246 | pub fn set_next_pixel(&mut self, layer: &mut Layer, color: Color) { 247 | layer.print_point_color_at(self.next_pixel % WIDTH, self.next_pixel / WIDTH, color); 248 | self.next_pixel = (self.next_pixel + 1) % (HEIGHT * WIDTH); 249 | } 250 | 251 | /// Sets the next column of the screen according to the passed audio data. 252 | pub fn set_next_col(&mut self, layer: &mut Layer, value0: u32, value1: u32) { 253 | let value0 = value0 + 2u32.pow(15); 254 | let value0 = value0 as u16 as usize; 255 | let value0 = value0 / 241; 256 | 257 | let value1 = value1 + 2u32.pow(15); 258 | let value1 = value1 as u16 as usize; 259 | let value1 = value1 / 241; 260 | 261 | for i in 0..HEIGHT { 262 | let mut color = Color::from_argb8888(0); 263 | 264 | if value0 >= self.prev_value.0 { 265 | if i >= self.prev_value.0 && i <= value0 { 266 | color.red = 0xff; 267 | color.alpha = 0xff; 268 | } 269 | } else if i <= self.prev_value.0 && i >= value0 { 270 | color.red = 0xff; 271 | color.alpha = 0xff; 272 | } 273 | 274 | if value1 >= self.prev_value.1 { 275 | if i >= self.prev_value.0 && i <= value1 { 276 | color.green = 0xff; 277 | color.alpha = 0xff; 278 | } 279 | } else if i <= self.prev_value.0 && i >= value1 { 280 | color.green = 0xff; 281 | color.alpha = 0xff; 282 | } 283 | 284 | let i = i as usize; 285 | layer.print_point_color_at(self.next_col, i, color); 286 | } 287 | 288 | self.next_col = (self.next_col + 1) % WIDTH; 289 | self.prev_value = (value0, value1); 290 | } 291 | } 292 | 293 | /// Allows writing text to the wrapped layer. 294 | /// 295 | /// This struct implements the [fmt::Write](core::fmt::Write) trait, which makes it possible 296 | /// to use the `writeln!` macro with this struct. 297 | pub struct TextWriter<'a, T: Framebuffer + 'a> { 298 | layer: &'a mut Layer, 299 | /// Column position of the cursor 300 | pub x_pos: usize, 301 | /// Row/Line position of the cursor 302 | pub y_pos: usize, 303 | } 304 | 305 | impl<'a, T: Framebuffer> TextWriter<'a, T> { 306 | fn newline(&mut self) { 307 | self.y_pos += 8; 308 | self.carriage_return() 309 | } 310 | fn carriage_return(&mut self) { 311 | self.x_pos = 0; 312 | } 313 | /// Erases all text on the screen 314 | pub fn clear(&mut self) { 315 | self.x_pos = 0; 316 | self.y_pos = 0; 317 | self.layer.clear(); 318 | } 319 | } 320 | 321 | impl<'a, T: Framebuffer> fmt::Write for TextWriter<'a, T> { 322 | fn write_str(&mut self, s: &str) -> fmt::Result { 323 | use font8x8::UnicodeFonts; 324 | 325 | for c in s.chars() { 326 | if c == '\n' { 327 | self.newline(); 328 | continue; 329 | } else if c == '\r' { 330 | self.carriage_return(); 331 | continue; 332 | } 333 | match c { 334 | ' '..='~' => { 335 | if self.x_pos >= WIDTH { 336 | self.newline(); 337 | } 338 | if self.y_pos >= HEIGHT { 339 | self.clear(); 340 | } 341 | let rendered = font8x8::BASIC_FONTS 342 | .get(c) 343 | .expect("character not found in basic font"); 344 | for (y, byte) in rendered.iter().enumerate() { 345 | for (x, bit) in (0..8).enumerate() { 346 | let alpha = if *byte & (1 << bit) == 0 { 0 } else { 255 }; 347 | let color = Color { 348 | red: 255, 349 | green: 255, 350 | blue: 255, 351 | alpha, 352 | }; 353 | self.layer 354 | .print_point_color_at(self.x_pos + x, self.y_pos + y, color); 355 | } 356 | } 357 | } 358 | _ => panic!("unprintable character"), 359 | } 360 | self.x_pos += 8; 361 | } 362 | Ok(()) 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/lcd/stdout.rs: -------------------------------------------------------------------------------- 1 | //! Initialize a LCD layer as standard output. 2 | 3 | use super::{FramebufferAl88, Layer, TextWriter}; 4 | use core::fmt; 5 | use cortex_m::interrupt; 6 | use spin::Mutex; 7 | 8 | static STDOUT: Stdout = Stdout(Mutex::new(None)); 9 | 10 | struct Stdout<'a>(Mutex>>); 11 | 12 | impl<'a> Stdout<'a> { 13 | fn with(&self, f: impl FnOnce(&mut Option>)) { 14 | interrupt::free(|_| f(&mut self.0.lock())) 15 | } 16 | } 17 | 18 | /// Erases the stdout output on the screen 19 | pub fn clear() { 20 | STDOUT.with(|stdout| { 21 | if let Some(ref mut stdout) = *stdout { 22 | stdout.clear(); 23 | } 24 | }); 25 | } 26 | 27 | /// Initialize the passed layer as standard output. 28 | /// 29 | /// Subsequent calls to [`print`](print) or [`println!`](println!) will then print 30 | /// to the layer. 31 | pub fn init(layer: Layer) { 32 | static mut LAYER: Option> = None; 33 | 34 | STDOUT.with(|stdout| { 35 | let layer = unsafe { LAYER.get_or_insert_with(|| layer) }; 36 | *stdout = Some(layer.text_writer()); 37 | }); 38 | } 39 | 40 | /// Prints to the LCD screen, appending a newline. 41 | /// 42 | /// The LCD stdout must be initialized. See the [`lcd::stdout::print`](lcd::stdout::print) 43 | /// function for more information. 44 | #[macro_export] 45 | macro_rules! println { 46 | () => (println!("")); 47 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 48 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 49 | } 50 | 51 | /// Prints to the LCD screen. 52 | /// 53 | /// The LCD stdout must be initialized. See the [`lcd::stdout::print`](lcd::stdout::print) 54 | /// function for more information. 55 | #[macro_export] 56 | macro_rules! print { 57 | ($($arg:tt)*) => ({ 58 | $crate::lcd::stdout::print(format_args!($($arg)*)); 59 | }); 60 | } 61 | 62 | /// Print to the standard output. 63 | /// 64 | /// The easiest way to use this function is through the `write!`/`writeln` macros. 65 | /// 66 | /// Panics if the standard output is not yet initialized. 67 | pub fn print(args: fmt::Arguments) { 68 | use core::fmt::Write; 69 | let mut uninitialized = false; 70 | STDOUT.with(|stdout| { 71 | if let Some(ref mut stdout) = *stdout { 72 | stdout.write_fmt(args).unwrap(); 73 | } else { 74 | uninitialized = true; 75 | } 76 | }); 77 | if uninitialized { 78 | panic!("stdout uninitialized") 79 | } 80 | } 81 | 82 | /// Returns whether the [`init`](init) function has already been called. 83 | pub fn is_initialized() -> bool { 84 | let mut initialized = false; 85 | STDOUT.with(|stdout| { 86 | initialized = stdout.is_some(); 87 | }); 88 | initialized 89 | } 90 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Device library for the STM32F746NG discovery board. 2 | //! 3 | //! Most of the device specific code is based on the stm32f746ng [reference manual], 4 | //! the [STM32CubeF7] package, and the [other stm32f746ng resources]. 5 | //! 6 | //! [reference manual]: https://www.st.com/resource/en/reference_manual/dm00124865.pdf 7 | //! [STM32CubeF7]: https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32cube-mcu-packages/stm32cubef7.html#getsoftware-scroll 8 | //! [other stm32f746ng resources]: https://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32-high-performance-mcus/stm32f7-series/stm32f7x6/stm32f746ng.html#design-scroll 9 | 10 | #![no_std] 11 | #![feature(trusted_len)] 12 | #![feature(optin_builtin_traits)] 13 | #![feature(generator_trait)] 14 | #![feature(arbitrary_self_types)] 15 | #![feature(drain_filter)] 16 | #![feature(never_type)] 17 | #![feature(generators)] 18 | #![feature(async_await)] 19 | #![feature(const_transmute)] 20 | #![feature(alloc_prelude)] 21 | #![warn(missing_docs)] 22 | 23 | #[macro_use] 24 | extern crate alloc; 25 | extern crate cortex_m_rt as rt; 26 | 27 | #[macro_use] 28 | pub mod lcd; 29 | pub mod ethernet; 30 | pub mod future_mutex; 31 | pub mod gpio; 32 | pub mod i2c; 33 | pub mod init; 34 | pub mod interrupts; 35 | pub mod mpsc_queue; 36 | pub mod random; 37 | pub mod sd; 38 | pub mod system_clock; 39 | pub mod task_runtime; 40 | pub mod touch; 41 | -------------------------------------------------------------------------------- /src/mpsc_queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! A mostly lock-free multi-producer, single consumer queue. 12 | //! 13 | //! This module contains an implementation of a concurrent MPSC queue. This 14 | //! queue can be used to share data between threads, and is also used as the 15 | //! building block of channels in rust. 16 | //! 17 | //! Note that the current implementation of this queue has a caveat of the `pop` 18 | //! method, and see the method for more information about it. Due to this 19 | //! caveat, this queue may not be appropriate for all use-cases. 20 | 21 | // http://www.1024cores.net/home/lock-free-algorithms 22 | // /queues/non-intrusive-mpsc-node-based-queue 23 | 24 | pub use self::PopResult::*; 25 | 26 | use alloc::boxed::Box; 27 | use core::cell::UnsafeCell; 28 | use core::ptr; 29 | use core::sync::atomic::{AtomicPtr, Ordering}; 30 | 31 | /// A result of the `pop` function. 32 | pub enum PopResult { 33 | /// Some data has been popped 34 | Data(T), 35 | /// The queue is empty 36 | Empty, 37 | /// The queue is in an inconsistent state. Popping data should succeed, but 38 | /// some pushers have yet to make enough progress in order allow a pop to 39 | /// succeed. It is recommended that a pop() occur "in the near future" in 40 | /// order to see if the sender has made progress or not 41 | Inconsistent, 42 | } 43 | 44 | struct Node { 45 | next: AtomicPtr>, 46 | value: Option, 47 | } 48 | 49 | /// The multi-producer single-consumer structure. This is not cloneable, but it 50 | /// may be safely shared so long as it is guaranteed that there is only one 51 | /// popper at a time (many pushers are allowed). 52 | pub struct Queue { 53 | head: AtomicPtr>, 54 | tail: UnsafeCell<*mut Node>, 55 | } 56 | 57 | unsafe impl Send for Queue {} 58 | unsafe impl Sync for Queue {} 59 | 60 | impl Node { 61 | unsafe fn new(v: Option) -> *mut Node { 62 | Box::into_raw(Box::new(Node { 63 | next: AtomicPtr::new(ptr::null_mut()), 64 | value: v, 65 | })) 66 | } 67 | } 68 | 69 | impl Queue { 70 | /// Creates a new queue that is safe to share among multiple producers and 71 | /// one consumer. 72 | pub fn new() -> Queue { 73 | let stub = unsafe { Node::new(None) }; 74 | Queue { 75 | head: AtomicPtr::new(stub), 76 | tail: UnsafeCell::new(stub), 77 | } 78 | } 79 | 80 | /// Pushes a new value onto this queue. 81 | pub fn push(&self, t: T) { 82 | unsafe { 83 | let n = Node::new(Some(t)); 84 | let prev = self.head.swap(n, Ordering::AcqRel); 85 | (*prev).next.store(n, Ordering::Release); 86 | } 87 | } 88 | 89 | /// Pops some data from this queue. 90 | /// 91 | /// Note that the current implementation means that this function cannot 92 | /// return `Option`. It is possible for this queue to be in an 93 | /// inconsistent state where many pushes have succeeded and completely 94 | /// finished, but pops cannot return `Some(t)`. This inconsistent state 95 | /// happens when a pusher is pre-empted at an inopportune moment. 96 | /// 97 | /// This inconsistent state means that this queue does indeed have data, but 98 | /// it does not currently have access to it at this time. 99 | pub fn pop(&self) -> PopResult { 100 | unsafe { 101 | let tail = *self.tail.get(); 102 | let next = (*tail).next.load(Ordering::Acquire); 103 | 104 | if !next.is_null() { 105 | *self.tail.get() = next; 106 | assert!((*tail).value.is_none()); 107 | assert!((*next).value.is_some()); 108 | let ret = (*next).value.take().unwrap(); 109 | let _: Box> = Box::from_raw(tail); 110 | return Data(ret); 111 | } 112 | 113 | if self.head.load(Ordering::Acquire) == tail { 114 | Empty 115 | } else { 116 | Inconsistent 117 | } 118 | } 119 | } 120 | } 121 | 122 | impl Drop for Queue { 123 | fn drop(&mut self) { 124 | unsafe { 125 | let mut cur = *self.tail.get(); 126 | while !cur.is_null() { 127 | let next = (*cur).next.load(Ordering::Relaxed); 128 | let _: Box> = Box::from_raw(cur); 129 | cur = next; 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/random.rs: -------------------------------------------------------------------------------- 1 | //! # Driver for the stm32f7_discovery rng module 2 | //! Use at your own risk. AND NOT FOR CRYPTOGRAPHIC PURPOSES !!!einself 3 | //! 4 | //! Example 5 | //! 6 | //! ``` 7 | //! let mut random_gen = rng::init(rng, rcc); 8 | //! match random_gen.poll_and_get() { 9 | //! 10 | //! Ok(random_number) => { 11 | //! println!("Got a random number {}", random_number); 12 | //! } 13 | //! 14 | //! Err(_) => { 15 | //! println!("Something went wrong"); 16 | //! } 17 | //! } 18 | //! ``` 19 | //! Since for disabling the rng, some rcc clock on the AHB2 Bus must be disabled as well. 20 | //! Therefore use .disable(rcc) after you are done. 21 | //! 22 | //! ``` 23 | //! random_gen.disable(rcc); 24 | //! ``` 25 | //! 26 | //! Iter is currently not implemented. Pull Requests welcome! 27 | 28 | use core::ops::Drop; 29 | use core::result::Result; 30 | use stm32f7::stm32f7x6::{RCC, RNG}; 31 | 32 | /// Contains state as well as the Rng Struct from embedded::board. 33 | pub struct Rng<'a> { 34 | last_number: u32, 35 | counter: u32, 36 | board_rng: &'a mut RNG, 37 | } 38 | 39 | /// Any of the errors (except AlreadyEnabled) can usually be resolved by initializing this 40 | /// struct again. 41 | #[derive(Debug)] 42 | pub enum ErrorType { 43 | /// The RNG_CLK was not correctly detected. 44 | ClockError, 45 | /// One of the following faulty sequences has been detected: 46 | /// 47 | /// - More than 64 consecutive bits at the same value (0 or 1) 48 | /// - More than 32 consecutive alternations of 0 and 1 (0101010101...01) 49 | SeedError, 50 | /// This bit is set at the same time as ClockError. 51 | ClockErrorInterrupt, 52 | /// This bit is set at the same time as SeedError. 53 | SeedErrorInterrupt, 54 | /// The RNG was already enabled. 55 | AlreadyEnabled, 56 | /// The RNG has no random data available yet. 57 | NotReady, 58 | } 59 | 60 | impl<'a> Rng<'a> { 61 | ///! This will take semi-ownership (with &'static) for the rng struct 62 | /// from board::rng. 63 | pub fn init(rng: &'a mut RNG, rcc: &mut RCC) -> Result { 64 | let control_register = rng.cr.read().rngen(); 65 | if control_register.bit_is_set() { 66 | return Err(ErrorType::AlreadyEnabled); 67 | } 68 | 69 | let rng = Rng { 70 | last_number: 0x0, 71 | counter: 0x0, 72 | board_rng: rng, 73 | }; 74 | rcc.ahb2enr.modify(|_, w| w.rngen().set_bit()); 75 | 76 | rng.board_rng.cr.modify(|_, w| { 77 | w.ie().clear_bit(); 78 | w.rngen().set_bit(); 79 | w 80 | }); 81 | 82 | Ok(rng) 83 | } 84 | 85 | /// For Testing purposes. Do not use except for debugging! 86 | pub fn tick(&mut self) -> u32 { 87 | self.poll_and_get().unwrap_or(0) 88 | } 89 | 90 | /// Actually try to acquire some random number 91 | /// Returns Ok(number) or Err! 92 | pub fn poll_and_get(&mut self) -> Result { 93 | let status = self.board_rng.sr.read(); 94 | 95 | if status.ceis().bit_is_set() { 96 | self.reset(); 97 | return Err(ErrorType::ClockErrorInterrupt); 98 | } 99 | if status.seis().bit_is_set() { 100 | self.reset(); 101 | return Err(ErrorType::SeedErrorInterrupt); 102 | } 103 | 104 | if status.cecs().bit_is_set() { 105 | return Err(ErrorType::ClockError); 106 | } 107 | if status.secs().bit_is_set() { 108 | self.reset(); 109 | return Err(ErrorType::SeedError); 110 | } 111 | if status.drdy().bit_is_set() { 112 | let data = self.board_rng.dr.read().rndata().bits(); 113 | if data != self.last_number { 114 | self.last_number = data; 115 | self.counter = 0; 116 | return Ok(data); 117 | } 118 | } 119 | self.counter += 1; 120 | if self.counter > 80 { 121 | self.reset(); 122 | self.counter = 0; 123 | } 124 | // data was not ready, try again! 125 | Err(ErrorType::NotReady) 126 | } 127 | 128 | /// Re-enable the RNG. 129 | pub fn reset(&mut self) { 130 | self.board_rng.cr.modify(|_, w| w.rngen().clear_bit()); 131 | self.board_rng.cr.modify(|_, w| w.ie().clear_bit()); 132 | self.board_rng.cr.modify(|_, w| w.rngen().set_bit()); 133 | } 134 | 135 | fn disable_cr(&mut self, rcc: &mut RCC) { 136 | self.board_rng.cr.modify(|_, w| w.rngen().clear_bit()); 137 | self.board_rng.cr.modify(|_, w| w.ie().clear_bit()); 138 | rcc.ahb2enr.modify(|_, w| w.rngen().clear_bit()); 139 | } 140 | 141 | /// Disable the RNG. 142 | pub fn disable(mut self, rcc: &mut RCC) { 143 | use core::mem; 144 | self.disable_cr(rcc); 145 | mem::forget(self); 146 | } 147 | } 148 | 149 | impl<'a> Drop for Rng<'a> { 150 | /// PANICS EVERYTIME! Use .disable(rcc) explicitly! 151 | fn drop(&mut self) { 152 | panic!("Use .disable() method on your random struct!"); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/sd/error.rs: -------------------------------------------------------------------------------- 1 | use bitflags::bitflags; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub enum Error { 5 | /// Unknown Error 6 | Error, 7 | /// No SD Card 8 | NoSdCard, 9 | /// Timeout while waiting for a response 10 | Timeout, 11 | /// Voltage Trial failed 12 | InvalidVoltrange, 13 | /// Card Error, see CardStatusFlags 14 | CardError { t: CardStatusFlags }, 15 | /// Response to a failed command 16 | SdmmcError { t: SdmmcErrorType }, 17 | /// Error during reading from/writing to the card 18 | RWError { t: RWErrorType }, 19 | } 20 | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 22 | pub enum SdmmcErrorType { 23 | /// CRC check failed 24 | CmdCrcFailed, 25 | /// No response to command 26 | CmdRespTimeout, 27 | } 28 | 29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 30 | pub enum RWErrorType { 31 | AddressOutOfRange, 32 | DataTimeout, 33 | DataCrcFailed, 34 | /// FIFO underrun 35 | TxUnderrun, 36 | /// FIFO overrun 37 | RxOverrun, 38 | } 39 | 40 | bitflags! { 41 | /// See Documentation Table 207 and Table 228 42 | pub struct CardStatusFlags: u32 { 43 | const OCR_ERROR_BITS = 0xFDFF_E008; 44 | const AKE_SEQ_ERROR = 0x0000_0008; 45 | const ERASE_RESET = 0x0000_2000; 46 | const CARD_ECC_DISABLED = 0x0000_4000; 47 | const WP_ERASE_SKIP = 0x0000_8000; 48 | const CID_CSD_OVERWRITE = 0x0001_0000; 49 | const ERROR = 0x0008_0000; 50 | const CC_ERROR = 0x0010_0000; 51 | const CARD_ECC_FAILED = 0x0020_0000; 52 | const ILLEGAL_COMMAND = 0x0040_0000; 53 | const COM_CRC_ERROR = 0x0080_0000; 54 | const LOCK_UNLOCK_FAILED = 0x0100_0000; 55 | const WP_VIOLATION = 0x0400_0000; 56 | const ERASE_PARAM = 0x0800_0000; 57 | const ERASE_SEQ_ERROR = 0x1000_0000; 58 | const BLOCK_LEN_ERROR = 0x2000_0000; 59 | const ADDRESS_MISALIGNED = 0x4000_0000; 60 | const ADDRESS_OUT_OF_RANGE = 0x8000_0000; 61 | 62 | // Other status bits 63 | const APP_CMD = 0x0000_0020; 64 | const SWITCH_ERROR = 0x0000_0080; 65 | const READY_FOR_DATA = 0x0000_0100; 66 | const CURRENT_STATE = 0x0000_1E00; 67 | const CARD_IS_LOCKED = 0x0200_0000; 68 | 69 | // R6 errors 70 | const R6_GENERAL_UNKNOWN_ERROR = 0x2000; 71 | const R6_ILLEGAL_COMMAND = 0x4000; 72 | const R6_CRC_FAILED = 0x8000; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/sd/init.rs: -------------------------------------------------------------------------------- 1 | use super::error::Error; 2 | use super::{sdmmc_cmd, CardInfo, CardType, Sd}; 3 | use crate::gpio::InputPin; 4 | use stm32f7::stm32f7x6::{RCC, SDMMC1}; 5 | 6 | /// Initializes the SD Card, if it is inserted and not already initialized. If the card is already 7 | /// initialized this function does nothing and returns no error. 8 | /// 9 | /// # Errors 10 | /// 11 | /// This function returns `NoSdCard` Error, if there is no SD Card inserted. This function also 12 | /// fails, if a command to the SDMMC-Controller fails. 13 | /// 14 | /// # Examples 15 | /// Initialization, if a card is inserted on startup. 16 | /// 17 | /// ```rust 18 | /// fn main(hw: board::Hardware) -> ! { 19 | /// // Setup board... 20 | /// 21 | /// let mut sd = sd::Sd::new(sdmmc, &mut gpio, rcc); 22 | /// sd::init(&mut sd).expect("Init failed"); 23 | /// 24 | /// loop {} 25 | /// } 26 | /// ``` 27 | /// 28 | /// On-the-fly (de-)initialization of the SD Card. 29 | /// 30 | /// ```rust 31 | /// fn main(hw: board::Hardware) -> ! { 32 | /// // Setup board... 33 | /// 34 | /// let mut sd = sd::Sd::new(sdmmc, &mut gpio, rcc); 35 | /// 36 | /// loop { 37 | /// if sd.card_present() && !sd.card_initialized() { 38 | /// if let Some(i_err) = sd::init(&mut sd).err() { 39 | /// hprintln!("{:?}", i_err); 40 | /// } 41 | /// } else if !sd.card_present() && sd.card_initialized() { 42 | /// sd::de_init(&mut sd); 43 | /// } 44 | /// } 45 | /// } 46 | /// ``` 47 | // TODO: Automate the (de-)initialization with interupts? 48 | pub fn init(sd: &mut Sd

) -> Result<(), Error> { 49 | // Check for SD card 50 | if !sd.card_present() { 51 | return Err(Error::NoSdCard); 52 | } 53 | 54 | // Card already initialized 55 | if sd.card_initialized() { 56 | return Ok(()); 57 | } 58 | 59 | // default clock configuration 60 | sd.sdmmc.clkcr.modify(|_, w| { 61 | w.negedge().clear_bit(); 62 | w.bypass().clear_bit(); 63 | w.pwrsav().clear_bit(); 64 | w.hwfc_en().clear_bit(); 65 | unsafe { 66 | w.widbus().bits(0); 67 | w.clkdiv().bits(0x76); 68 | } 69 | w 70 | }); 71 | 72 | let mut card_info = CardInfo::default(); 73 | card_info.card_type = power_on(sd.sdmmc)?; 74 | 75 | // Let the card send the CID and enter identification process 76 | sdmmc_cmd::send_cid(sd.sdmmc)?; 77 | 78 | // Get the RCA of the card 79 | card_info.rca = sdmmc_cmd::set_rel_add(sd.sdmmc)?; 80 | 81 | sdmmc_cmd::send_csd(sd.sdmmc, u32::from(card_info.rca) << 16)?; 82 | 83 | let csd = [ 84 | sd.sdmmc.resp1.read().cardstatus1().bits(), 85 | sd.sdmmc.resp2.read().cardstatus2().bits(), 86 | sd.sdmmc.resp3.read().cardstatus3().bits(), 87 | sd.sdmmc.resp4.read().cardstatus4().bits(), 88 | ]; 89 | 90 | get_card_csd(&mut card_info, csd); 91 | 92 | sdmmc_cmd::sel_desel(sd.sdmmc, u32::from(card_info.rca) << 16)?; 93 | 94 | sd.card_info = Some(card_info); 95 | 96 | Ok(()) 97 | } 98 | 99 | /// Deinitializes the SD Card. 100 | pub fn de_init(sd: &mut Sd

) { 101 | sd.card_info = None; 102 | 103 | sd.sdmmc 104 | .power 105 | .modify(|_, w| unsafe { w.pwrctrl().bits(0x00) }); 106 | } 107 | 108 | /// Initializes the hardware, including the clocks used by the SDMMC-Controller. 109 | pub fn init_hw(rcc: &mut RCC) { 110 | // Enable SDMMC1 clock 111 | rcc.apb2enr.modify(|_, w| w.sdmmc1en().enabled()); 112 | 113 | // wait for enabling 114 | while !rcc.apb2enr.read().sdmmc1en().is_enabled() {} 115 | } 116 | 117 | fn power_on(sdmmc: &mut SDMMC1) -> Result { 118 | // power up the card 119 | sdmmc.clkcr.modify(|_, w| w.clken().clear_bit()); 120 | sdmmc.power.modify(|_, w| unsafe { w.pwrctrl().bits(0x03) }); 121 | sdmmc.clkcr.modify(|_, w| w.clken().set_bit()); 122 | 123 | let mut card_type = CardType::SDv1; 124 | 125 | // set sd card to idle state 126 | sdmmc_cmd::idle(sdmmc, 5000)?; 127 | 128 | // get Card version and operation voltage 129 | let mut count = 0; 130 | let max_volt_trial = 0xFFFF; 131 | let mut valid_voltage = false; 132 | if sdmmc_cmd::oper_cond(sdmmc).is_ok() { 133 | let mut card_status = 0; 134 | // voltage trial for card V2 135 | while !valid_voltage { 136 | if count == max_volt_trial { 137 | return Err(Error::InvalidVoltrange); 138 | } 139 | count += 1; 140 | 141 | // Send CMD55, needed for next CMD. 142 | sdmmc_cmd::app(sdmmc, 0)?; 143 | 144 | // Send ACMD41. 0x40..0 for high capacity. 145 | sdmmc_cmd::app_oper(sdmmc, 0x4000_0000)?; 146 | 147 | card_status = sdmmc.resp1.read().cardstatus1().bits(); 148 | 149 | valid_voltage = card_status >> 31 == 1 150 | } 151 | // determine whether high or standard capacity. 152 | if card_status & 0x4000_0000 != 0 { 153 | card_type = CardType::SDv2HC; 154 | } else { 155 | card_type = CardType::SDv2SC; 156 | } 157 | } else { 158 | while !valid_voltage { 159 | if count == max_volt_trial { 160 | return Err(Error::InvalidVoltrange); 161 | } 162 | count += 1; 163 | 164 | // Send CMD55, needed for next CMD. 165 | sdmmc_cmd::app(sdmmc, 0)?; 166 | 167 | // Send ACMD41. 0x0 for standard capacity. 168 | sdmmc_cmd::app_oper(sdmmc, 0x0)?; 169 | 170 | let card_status = sdmmc.resp1.read().cardstatus1().bits(); 171 | 172 | valid_voltage = card_status >> 31 == 1 173 | } 174 | } 175 | 176 | Ok(card_type) 177 | } 178 | 179 | fn get_card_csd(card_info: &mut CardInfo, csd: [u32; 4]) { 180 | if card_info.card_type == CardType::SDv2HC { 181 | let tmp = csd[1] & 0xFF; 182 | let mut device_size = (tmp & 0x3F) << 16; 183 | 184 | let tmp = (csd[2] & 0xFFFF_0000) >> 16; 185 | device_size |= tmp; 186 | 187 | card_info.blk_number = (device_size + 1) * 1024; 188 | card_info.log_blk_number = card_info.blk_number; 189 | card_info.blk_size = 512; 190 | card_info.log_blk_size = card_info.blk_size; 191 | } else { 192 | let tmp = csd[1] & 0x3FF; 193 | let mut device_size = tmp << 2; 194 | 195 | let tmp = (csd[2] & 0xFF00_0000) >> 24; 196 | device_size |= (tmp & 0xC0) >> 6; 197 | 198 | let device_size_mul = (csd[2] & 0x0003_8000) >> 15; 199 | 200 | let rd_blk_len = (csd[1] & 0x000F_0000) >> 16; 201 | 202 | card_info.blk_number = (device_size + 1) * (1 << (device_size_mul + 2)); 203 | card_info.blk_size = 1 << rd_blk_len; 204 | card_info.log_blk_number = card_info.blk_number * (card_info.blk_size / 512); 205 | card_info.log_blk_size = 512; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/sd/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provides functions to detect and initialize SD cards. 2 | //! 3 | //! **This module is currently untested!** 4 | 5 | #![allow(missing_docs)] 6 | 7 | pub use self::init::{de_init, init}; 8 | 9 | pub mod error; 10 | mod init; 11 | mod sdmmc_cmd; 12 | 13 | use self::error::*; 14 | use crate::gpio::InputPin; 15 | use alloc::vec::Vec; 16 | use core::cmp::min; 17 | use stm32f7::stm32f7x6::{RCC, SDMMC1}; 18 | 19 | /// SD handle. 20 | pub struct Sd<'a, PresentPin: InputPin + 'a> { 21 | sdmmc: &'a mut SDMMC1, 22 | card_info: Option, 23 | present_pin: &'a PresentPin, 24 | } 25 | 26 | impl<'a, PresentPin: InputPin> Sd<'a, PresentPin> { 27 | /// Creates a new SD handle. It initializes the hardware, but not the card. To initialize the 28 | /// card a seperate call to `sd::init()` is necessary. 29 | /// This function returns a SD handle whether or not a SD Card is inserted. 30 | /// 31 | /// # Examples 32 | /// ```rust 33 | /// fn main(hw: board::Hardware) -> ! { 34 | /// // Setup board... 35 | /// 36 | /// // Create SD handle 37 | /// let mut sd = sd::Sd::new(sdmmc, &mut gpio, rcc); 38 | /// // Initialize SD Card 39 | /// if let Some(i_err) = sd::init(&mut sd).err() { 40 | /// hprintln!("{:?}", i_err); 41 | /// } 42 | /// 43 | /// loop {} 44 | /// } 45 | /// ``` 46 | pub fn new(sdmmc: &'a mut SDMMC1, rcc: &mut RCC, present_pin: &'a PresentPin) -> Self { 47 | self::init::init_hw(rcc); 48 | 49 | Sd { 50 | sdmmc, 51 | card_info: None, 52 | present_pin, 53 | } 54 | } 55 | 56 | /// Returns `None` if the card is not initialized or `Some(CardInfo)` if the card is 57 | /// initialized. 58 | pub fn get_card_info(&self) -> &Option { 59 | &self.card_info 60 | } 61 | 62 | /// Returns true if a SD Card is inserted. 63 | pub fn card_present(&self) -> bool { 64 | !self.present_pin.get() 65 | } 66 | 67 | /// Returns true if the SD Card is initialized. More precisely it returns, whether the 68 | /// `CardInfo` is not `None`. 69 | pub fn card_initialized(&self) -> bool { 70 | self.card_info.is_some() 71 | } 72 | 73 | /// Reads `number_of_blks` blocks at address `block_add` from the SD Card. A block has a size of 512 74 | /// Byte. 75 | /// 76 | /// # Errors 77 | /// 78 | /// Returns an Error if a command to the SDMMC-Controller fails or a timeout occurs. 79 | /// 80 | /// # Examples 81 | /// ```rust 82 | /// fn main(hw: board::Hardware) -> ! { 83 | /// // Setup board... 84 | /// 85 | /// let mut sd = sd::Sd::new(sdmmc, &mut gpio, rcc); 86 | /// sd::init(&mut sd).expect("Init failed"); 87 | /// 88 | /// match sd.read_blocks(42, 2) { 89 | /// Ok(data) => { 90 | /// assert!(data.len() == 256); 91 | /// hprintln!("{:?}", data); 92 | /// }, 93 | /// Err(r_err) => hprintln!("{:?}", r_err); 94 | /// } 95 | /// 96 | /// loop {} 97 | /// } 98 | /// ``` 99 | pub fn read_blocks(&mut self, block_add: u32, number_of_blks: u16) -> Result, Error> { 100 | // This is a wrapper function for the read_blocks_h() function. The read_blocks_h() 101 | // function can only read single blocks from the card, because the multi-block mode of the 102 | // SDMMC-Controller doesn't work. 103 | let mut data = vec![]; 104 | for i in 0..u32::from(number_of_blks) { 105 | let mut block = self.read_blocks_h(block_add + i, 1, 5000)?; 106 | data.append(&mut block); 107 | } 108 | 109 | Ok(data) 110 | } 111 | 112 | /// Writes the content of `data` to `number_of_blks` blocks at address `block_add` to the SD 113 | /// Card. A block has a size of 512 Byte. If the `data` slice contains more data, then the 114 | /// specified number of blocks can save, the rest of the data will not be written to the card. 115 | /// If the `data` slice is empty or contains less data, then the specified number of blocks can 116 | /// save, the rest of the blocks get filled with 0s. 117 | /// 118 | /// # Errors 119 | /// 120 | /// Returns an Error if a command to the SDMMC-Controller fails or a timeout occurs. 121 | /// 122 | /// # Examples 123 | /// ```rust 124 | /// fn main(hw: board::Hardware) -> ! { 125 | /// // Setup board... 126 | /// 127 | /// let mut sd = sd::Sd::new(sdmmc, &mut gpio, rcc); 128 | /// sd::init(&mut sd).expect("Init failed"); 129 | /// 130 | /// let data = vec![0; 256]; 131 | /// 132 | /// if let Some(w_err) = sd.write_blocks(&data[..], 42, 2) { 133 | /// hprintln!("{:?}", w_err); 134 | /// } 135 | /// 136 | /// loop {} 137 | /// } 138 | /// ``` 139 | pub fn write_blocks( 140 | &mut self, 141 | data: &[u32], 142 | block_add: u32, 143 | number_of_blks: u16, 144 | ) -> Result<(), Error> { 145 | // This is a wrapper function for the write_blocks_h() function. The write_blocks_h() 146 | // function can only write single blocks to the card, because the multi-block mode of the 147 | // SDMMC-Controller doesn't work. 148 | for i in 0..u32::from(number_of_blks) { 149 | self.write_blocks_h( 150 | &data[min((i as usize) * 128, data.len())..], 151 | block_add + i, 152 | 1, 153 | 5000, 154 | )?; 155 | } 156 | 157 | Ok(()) 158 | } 159 | 160 | // This function doesn't support multi-block read. See read_blocks(). 161 | fn read_blocks_h( 162 | &mut self, 163 | block_add: u32, 164 | number_of_blks: u16, 165 | timeout: u32, 166 | ) -> Result, Error> { 167 | // No blocks to read -> return empty vector 168 | if number_of_blks == 0 { 169 | return Ok(vec![]); 170 | } 171 | // Check if a SD Card is inserted. 172 | if !self.card_present() { 173 | return Err(Error::NoSdCard); 174 | } 175 | let mut block_add = block_add; 176 | let card_info = self.card_info.as_ref().unwrap(); 177 | 178 | // Check if the blocks to read are in bounds. 179 | if block_add + u32::from(number_of_blks) > card_info.log_blk_number { 180 | return Err(Error::RWError { 181 | t: RWErrorType::AddressOutOfRange, 182 | }); 183 | } 184 | 185 | // On high capacity cards the block_add has to be in bytes and not the block number itself. 186 | if card_info.card_type == CardType::SDv2HC { 187 | block_add *= card_info.log_blk_size; 188 | } 189 | 190 | // Tell the sdmmc the block length... 191 | sdmmc_cmd::block_length(self.sdmmc, card_info.log_blk_size)?; 192 | // ...and if a single or multiple block should be read 193 | // TODO: multi-block read doesn't seem to work with the SDMMC-Controller 194 | if number_of_blks > 1 { 195 | sdmmc_cmd::read_multi_blk(self.sdmmc, block_add)?; 196 | } else { 197 | sdmmc_cmd::read_single_blk(self.sdmmc, block_add)?; 198 | } 199 | 200 | // Set up the Data Path State Machine (DPSM) 201 | let data_length = u32::from(number_of_blks) * card_info.log_blk_size; 202 | self.sdmmc 203 | .dlen 204 | .modify(|_, w| unsafe { w.datalength().bits(data_length) }); 205 | self.sdmmc 206 | .dtimer 207 | .modify(|_, w| unsafe { w.datatime().bits(0xFFFF_FFFF) }); 208 | self.sdmmc.dctrl.modify(|_, w| { 209 | unsafe { w.dblocksize().bits(0x09) }; // blocksize = 2^n => blocksize = 2^9 = 512 210 | w.dtdir().set_bit(); // direction: false -> write, true -> read 211 | w.dtmode().clear_bit(); // mode: false -> block, true -> stream 212 | w.dten().set_bit(); // enable data transfer 213 | w 214 | }); 215 | 216 | // Read data from the SD Card, until dataend is reached or an error occurs 217 | let mut data = vec![]; 218 | let timeout = crate::system_clock::ms() as u32 + timeout; 219 | while (crate::system_clock::ms() as u32) < timeout 220 | && self.sdmmc.sta.read().rxoverr().bit_is_clear() 221 | && self.sdmmc.sta.read().dcrcfail().bit_is_clear() 222 | && self.sdmmc.sta.read().dtimeout().bit_is_clear() 223 | && self.sdmmc.sta.read().dataend().bit_is_clear() 224 | { 225 | if self.sdmmc.sta.read().rxfifohf().bit_is_set() { 226 | for _ in 0..8 { 227 | data.push(self.sdmmc.fifo.read().fifodata().bits()); 228 | } 229 | } 230 | } 231 | 232 | if (crate::system_clock::ms() as u32) >= timeout { 233 | return Err(Error::Timeout); 234 | } 235 | 236 | // Needed in multi-block mode to stop the transmission. 237 | if self.sdmmc.sta.read().dataend().bit_is_set() && number_of_blks > 1 { 238 | sdmmc_cmd::stop_transfer(self.sdmmc)?; 239 | } 240 | 241 | // Check for errors 242 | if self.sdmmc.sta.read().dtimeout().bit_is_set() { 243 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 244 | return Err(Error::RWError { 245 | t: RWErrorType::DataTimeout, 246 | }); 247 | } 248 | if self.sdmmc.sta.read().dcrcfail().bit_is_set() { 249 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 250 | return Err(Error::RWError { 251 | t: RWErrorType::DataCrcFailed, 252 | }); 253 | } 254 | if self.sdmmc.sta.read().rxoverr().bit_is_set() { 255 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 256 | return Err(Error::RWError { 257 | t: RWErrorType::RxOverrun, 258 | }); 259 | } 260 | 261 | // If there is still valid data in the FIFO, empty the FIFO 262 | while (crate::system_clock::ms() as u32) < timeout 263 | && self.sdmmc.sta.read().rxdavl().bit_is_set() 264 | { 265 | data.push(self.sdmmc.fifo.read().fifodata().bits()); 266 | } 267 | 268 | if (crate::system_clock::ms() as u32) >= timeout { 269 | return Err(Error::Timeout); 270 | } 271 | 272 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 273 | 274 | Ok(data) 275 | } 276 | 277 | // This function doesn't support multi-block write. See write_blocks(). 278 | fn write_blocks_h( 279 | &mut self, 280 | data: &[u32], 281 | block_add: u32, 282 | number_of_blks: u16, 283 | timeout: u32, 284 | ) -> Result<(), Error> { 285 | // No blocks to read -> return empty vector 286 | if number_of_blks == 0 { 287 | return Ok(()); 288 | } 289 | // Check if a SD Card is inserted. 290 | if !self.card_present() { 291 | return Err(Error::NoSdCard); 292 | } 293 | let mut block_add = block_add; 294 | let card_info = self.card_info.as_ref().unwrap(); 295 | 296 | // Check if the blocks to read are in bounds. 297 | if block_add + u32::from(number_of_blks) > card_info.log_blk_number { 298 | return Err(Error::RWError { 299 | t: RWErrorType::AddressOutOfRange, 300 | }); 301 | } 302 | 303 | // On high capacity cards the block_add has to be in bytes and not the block number itself. 304 | if card_info.card_type == CardType::SDv2HC { 305 | block_add *= card_info.log_blk_size; 306 | } 307 | 308 | // Tell the sdmmc the block length... 309 | sdmmc_cmd::block_length(self.sdmmc, card_info.log_blk_size)?; 310 | // ...and if a single or multiple block should be written 311 | // TODO: multi-block write doesn't seem to work with the SDMMC-Controller 312 | if number_of_blks > 1 { 313 | sdmmc_cmd::write_multi_blk(self.sdmmc, block_add)?; 314 | } else { 315 | sdmmc_cmd::write_single_blk(self.sdmmc, block_add)?; 316 | } 317 | 318 | // Set up the Data Path State Machine (DPSM) 319 | let data_length = u32::from(number_of_blks) * card_info.log_blk_size; 320 | self.sdmmc 321 | .dlen 322 | .modify(|_, w| unsafe { w.datalength().bits(data_length) }); 323 | self.sdmmc 324 | .dtimer 325 | .modify(|_, w| unsafe { w.datatime().bits(0xFFFF_FFFF) }); 326 | self.sdmmc.dctrl.modify(|_, w| { 327 | unsafe { w.dblocksize().bits(0x09) }; // blocksize = 2^n => blocksize = 2^9 = 512 328 | w.dtdir().clear_bit(); // direction: false -> write, true -> read 329 | w.dtmode().clear_bit(); // mode: false -> block, true -> stream 330 | w.dten().set_bit(); // enable data transfer 331 | w 332 | }); 333 | 334 | // Write data to the SD Card, until dataend is reached or an error occurs 335 | let mut data_counter = 0; 336 | let timeout = crate::system_clock::ms() as u32 + timeout; 337 | while (crate::system_clock::ms() as u32) < timeout 338 | && self.sdmmc.sta.read().txunderr().bit_is_clear() 339 | && self.sdmmc.sta.read().dcrcfail().bit_is_clear() 340 | && self.sdmmc.sta.read().dtimeout().bit_is_clear() 341 | && self.sdmmc.sta.read().dataend().bit_is_clear() 342 | { 343 | if self.sdmmc.sta.read().txfifohe().bit_is_set() { 344 | // If there is no more data to write, but the sdmmc controller has not reached 345 | // dataend yet, write 0s to the FIFO 346 | let mut pad_data: &[u32] = &[0; 8][..]; 347 | if data_counter < data.len() { 348 | pad_data = &data[data_counter..min(data_counter + 8, data.len())]; 349 | data_counter += 8; 350 | } 351 | for d in pad_data { 352 | self.sdmmc 353 | .fifo 354 | .modify(|_, w| unsafe { w.fifodata().bits(*d) }); 355 | } 356 | } 357 | } 358 | 359 | if (crate::system_clock::ms() as u32) >= timeout { 360 | return Err(Error::Timeout); 361 | } 362 | 363 | // Needed in multi-block mode to stop the transmission 364 | if self.sdmmc.sta.read().dataend().bit_is_set() && number_of_blks > 1 { 365 | sdmmc_cmd::stop_transfer(self.sdmmc)?; 366 | } 367 | 368 | // Wait a bit for the controller to end the write process. 369 | let wait = crate::system_clock::ms() + 100; 370 | while crate::system_clock::ms() < wait {} 371 | 372 | // Check for errors 373 | if self.sdmmc.sta.read().dtimeout().bit_is_set() { 374 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 375 | return Err(Error::RWError { 376 | t: RWErrorType::DataTimeout, 377 | }); 378 | } 379 | if self.sdmmc.sta.read().dcrcfail().bit_is_set() { 380 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 381 | return Err(Error::RWError { 382 | t: RWErrorType::DataCrcFailed, 383 | }); 384 | } 385 | if self.sdmmc.sta.read().txunderr().bit_is_set() { 386 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 387 | return Err(Error::RWError { 388 | t: RWErrorType::TxUnderrun, 389 | }); 390 | } 391 | 392 | sdmmc_cmd::clear_all_static_status_flags(self.sdmmc); 393 | 394 | Ok(()) 395 | } 396 | } 397 | 398 | /// Different SD card versions. 399 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 400 | pub enum CardType { 401 | /// SD version 1, always Standard Capacity (SD) 402 | SDv1, 403 | /// SD version 2 with SD (up to 2 GB) 404 | SDv2SC, 405 | /// SD version 2 with High Capacity (HC) (up to 32 GB) or Extended Capacity (XC) (up to 2 TB) 406 | SDv2HC, 407 | } 408 | 409 | /// Various information about the SD card. 410 | #[derive(Debug)] 411 | pub struct CardInfo { 412 | /// The type of the card. 413 | card_type: CardType, 414 | /// Relative Card Address 415 | rca: u16, 416 | /// Number of physical blocks 417 | blk_number: u32, 418 | /// Physical block size 419 | blk_size: u32, 420 | /// Number of logical blocks 421 | log_blk_number: u32, 422 | /// Logical block size 423 | log_blk_size: u32, 424 | } 425 | 426 | impl Default for CardInfo { 427 | fn default() -> CardInfo { 428 | CardInfo { 429 | card_type: CardType::SDv2HC, 430 | rca: 0, 431 | blk_number: 0, 432 | blk_size: 0, 433 | log_blk_number: 0, 434 | log_blk_size: 0, 435 | } 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /src/sd/sdmmc_cmd.rs: -------------------------------------------------------------------------------- 1 | use super::error::*; 2 | use stm32f7::stm32f7x6::SDMMC1; 3 | 4 | // Initialization commands 5 | /// Set the SD card into idle state 6 | pub fn idle(sdmmc: &mut SDMMC1, timeout: u32) -> Result<(), Error> { 7 | send_cmd(sdmmc, 0, 0x00, true, false, 0x00); 8 | 9 | let timeout = crate::system_clock::ms() as u32 + timeout; 10 | while (crate::system_clock::ms() as u32) < timeout && sdmmc.sta.read().cmdsent().bit_is_clear() 11 | { 12 | } 13 | 14 | if (crate::system_clock::ms() as u32) >= timeout { 15 | return Err(Error::Timeout); 16 | } 17 | 18 | Ok(()) 19 | } 20 | 21 | /// Send CMD55 to signalize that the next command is an app command 22 | pub fn app(sdmmc: &mut SDMMC1, argument: u32) -> Result<(), Error> { 23 | send_cmd(sdmmc, argument, 55, true, false, 0x01); 24 | 25 | get_cmd_resp1(sdmmc, 55, 5000) 26 | } 27 | 28 | /// Send ACMD41 to get the operation condition register (OCR) of the card. 29 | /// Always send CMD55 before sending this command. 30 | pub fn app_oper(sdmmc: &mut SDMMC1, capacity: u32) -> Result<(), Error> { 31 | send_cmd(sdmmc, 0x8010_0000 | capacity, 41, true, false, 0x01); 32 | 33 | get_cmd_resp3(sdmmc, 5000) 34 | } 35 | 36 | /// Get the Operation Condition of the card. This command is only supported 37 | /// by SD card v2 and can therefore be used to determine the version of the card. 38 | pub fn oper_cond(sdmmc: &mut SDMMC1) -> Result<(), Error> { 39 | send_cmd(sdmmc, 0x1AA, 8, true, false, 0x01); 40 | 41 | wait_resp(sdmmc, 5000)?; 42 | 43 | sdmmc.icr.modify(|_, w| w.cmdrendc().set_bit()); 44 | 45 | Ok(()) 46 | } 47 | 48 | /// Get the Card Indentification Number (CID) of the card. (CMD2) 49 | pub fn send_cid(sdmmc: &mut SDMMC1) -> Result<(), Error> { 50 | send_cmd(sdmmc, 0, 2, true, false, 0x03); 51 | 52 | get_cmd_resp2(sdmmc, 5000) 53 | } 54 | 55 | /// Get the Relative Card Address (RCA) of the card. This number is shorter 56 | /// than the CID. (CMD3) 57 | pub fn set_rel_add(sdmmc: &mut SDMMC1) -> Result { 58 | send_cmd(sdmmc, 0, 3, true, false, 0x01); 59 | 60 | get_cmd_resp6(sdmmc, 3, 5000) 61 | } 62 | 63 | pub fn send_csd(sdmmc: &mut SDMMC1, rca: u32) -> Result<(), Error> { 64 | send_cmd(sdmmc, rca, 9, true, false, 0x03); 65 | 66 | get_cmd_resp2(sdmmc, 5000) 67 | } 68 | 69 | pub fn sel_desel(sdmmc: &mut SDMMC1, rca: u32) -> Result<(), Error> { 70 | send_cmd(sdmmc, rca, 7, true, false, 0x01); 71 | 72 | get_cmd_resp1(sdmmc, 7, 5000) 73 | } 74 | 75 | // Read/Write commands 76 | /// Set the block length of the blocks to read/write. 77 | pub fn block_length(sdmmc: &mut SDMMC1, block_size: u32) -> Result<(), Error> { 78 | send_cmd(sdmmc, block_size, 16, true, false, 0x01); 79 | 80 | get_cmd_resp1(sdmmc, 16, 5000) 81 | } 82 | 83 | /// Instruct the controller, that a single block will be written. 84 | pub fn write_single_blk(sdmmc: &mut SDMMC1, block_add: u32) -> Result<(), Error> { 85 | send_cmd(sdmmc, block_add, 24, true, false, 0x01); 86 | 87 | get_cmd_resp1(sdmmc, 24, 5000) 88 | } 89 | 90 | /// Instruct the controller, that multiple blocks will be written. End the write process with a 91 | /// call to `stop_transfer()`. 92 | // TODO: This doesn't seem to work... 93 | pub fn write_multi_blk(sdmmc: &mut SDMMC1, block_add: u32) -> Result<(), Error> { 94 | send_cmd(sdmmc, block_add, 25, true, false, 0x01); 95 | 96 | get_cmd_resp1(sdmmc, 25, 5000) 97 | } 98 | 99 | /// Instruct the controller, that a single block will be read. 100 | pub fn read_single_blk(sdmmc: &mut SDMMC1, block_add: u32) -> Result<(), Error> { 101 | send_cmd(sdmmc, block_add, 17, true, false, 0x01); 102 | 103 | get_cmd_resp1(sdmmc, 17, 5000) 104 | } 105 | 106 | /// Instruct the controller, that multiple blocks will be read. End the read process with a 107 | /// call to `stop_transfer()`. 108 | // TODO: This doesn't seem to work... 109 | pub fn read_multi_blk(sdmmc: &mut SDMMC1, block_add: u32) -> Result<(), Error> { 110 | send_cmd(sdmmc, block_add, 18, true, false, 0x01); 111 | 112 | get_cmd_resp1(sdmmc, 18, 5000) 113 | } 114 | 115 | // An alternative, to end multi-block read/write with `stop_transfer()`, is to specify the number of 116 | // blocks that should be written beforehand. 117 | // The controller doesn't seem to accept this command and always returns with a CmdRespTimeout Error. 118 | // pub fn set_blk_count(sdmmc: &mut SDMMC1, number_of_blks: u16) -> Result<(), Error> { 119 | // send_cmd(sdmmc, number_of_blks as u32, 23, true, false, 0x01); 120 | // 121 | // get_cmd_resp1(sdmmc, 23, 5000) 122 | // } 123 | 124 | /// Stops the tranfer to the card after a multi-block read/write. 125 | pub fn stop_transfer(sdmmc: &mut SDMMC1) -> Result<(), Error> { 126 | send_cmd(sdmmc, 0, 12, true, false, 0x01); 127 | 128 | get_cmd_resp1(sdmmc, 12, 5000)?; 129 | 130 | Ok(()) 131 | } 132 | 133 | /// Send a command to the card. 134 | pub fn send_cmd( 135 | sdmmc: &mut SDMMC1, 136 | argument: u32, 137 | cmdidx: u8, 138 | cpsmen: bool, 139 | waitint: bool, 140 | waitresp: u8, 141 | ) { 142 | sdmmc 143 | .arg 144 | .modify(|_, w| unsafe { w.cmdarg().bits(argument) }); 145 | sdmmc.cmd.modify(|_, w| { 146 | w.cpsmen().bit(cpsmen); 147 | w.waitint().bit(waitint); 148 | unsafe { 149 | w.waitresp().bits(waitresp); 150 | w.cmdindex().bits(cmdidx); 151 | } 152 | w 153 | }); 154 | } 155 | 156 | // Command responses from the controller 157 | fn get_cmd_resp1(sdmmc: &mut SDMMC1, cmd_idx: u8, timeout: u32) -> Result<(), Error> { 158 | wait_resp_crc(sdmmc, timeout)?; 159 | 160 | if sdmmc.respcmd.read().respcmd().bits() != cmd_idx { 161 | return Err(Error::SdmmcError { 162 | t: SdmmcErrorType::CmdCrcFailed, 163 | }); 164 | } 165 | 166 | clear_all_static_status_flags(sdmmc); 167 | 168 | // Get response and check card status for errors 169 | let card_status = sdmmc.resp1.read().cardstatus1().bits(); 170 | 171 | check_for_errors(card_status)?; 172 | 173 | Ok(()) 174 | } 175 | 176 | fn get_cmd_resp2(sdmmc: &mut SDMMC1, timeout: u32) -> Result<(), Error> { 177 | wait_resp_crc(sdmmc, timeout)?; 178 | 179 | clear_all_static_status_flags(sdmmc); 180 | 181 | Ok(()) 182 | } 183 | 184 | fn get_cmd_resp3(sdmmc: &mut SDMMC1, timeout: u32) -> Result<(), Error> { 185 | wait_resp(sdmmc, timeout)?; 186 | 187 | clear_all_static_status_flags(sdmmc); 188 | 189 | Ok(()) 190 | } 191 | 192 | fn get_cmd_resp6(sdmmc: &mut SDMMC1, cmd_idx: u8, timeout: u32) -> Result { 193 | wait_resp_crc(sdmmc, timeout)?; 194 | 195 | if sdmmc.respcmd.read().respcmd().bits() != cmd_idx { 196 | return Err(Error::SdmmcError { 197 | t: SdmmcErrorType::CmdCrcFailed, 198 | }); 199 | } 200 | 201 | clear_all_static_status_flags(sdmmc); 202 | 203 | // Get response and check card status for errors 204 | let card_status = sdmmc.resp1.read().cardstatus1().bits(); 205 | 206 | if card_status 207 | & (CardStatusFlags::R6_CRC_FAILED 208 | | CardStatusFlags::R6_ILLEGAL_COMMAND 209 | | CardStatusFlags::R6_GENERAL_UNKNOWN_ERROR) 210 | .bits() 211 | == 0 212 | { 213 | Ok((card_status >> 16) as u16) 214 | } else if card_status & CardStatusFlags::R6_CRC_FAILED.bits() != 0 { 215 | Err(Error::CardError { 216 | t: CardStatusFlags::R6_CRC_FAILED, 217 | }) 218 | } else if card_status & CardStatusFlags::R6_ILLEGAL_COMMAND.bits() != 0 { 219 | Err(Error::CardError { 220 | t: CardStatusFlags::R6_ILLEGAL_COMMAND, 221 | }) 222 | } else { 223 | Err(Error::CardError { 224 | t: CardStatusFlags::R6_GENERAL_UNKNOWN_ERROR, 225 | }) 226 | } 227 | } 228 | 229 | // Wait for the Controller to respond to a command. 230 | fn wait_resp(sdmmc: &mut SDMMC1, timeout: u32) -> Result<(), Error> { 231 | let timeout = crate::system_clock::ms() as u32 + timeout; 232 | while (crate::system_clock::ms() as u32) < timeout 233 | && sdmmc.sta.read().cmdrend().bit_is_clear() 234 | && sdmmc.sta.read().ccrcfail().bit_is_clear() 235 | && sdmmc.sta.read().ctimeout().bit_is_clear() 236 | {} 237 | 238 | if (crate::system_clock::ms() as u32) >= timeout { 239 | return Err(Error::Timeout); 240 | } 241 | 242 | if sdmmc.sta.read().ctimeout().bit_is_set() { 243 | sdmmc.icr.modify(|_, w| w.ctimeoutc().set_bit()); 244 | return Err(Error::SdmmcError { 245 | t: SdmmcErrorType::CmdRespTimeout, 246 | }); 247 | } 248 | 249 | Ok(()) 250 | } 251 | 252 | // Similiar to wait_resp(), but also checks the CRC afterwards 253 | fn wait_resp_crc(sdmmc: &mut SDMMC1, timeout: u32) -> Result<(), Error> { 254 | wait_resp(sdmmc, timeout)?; 255 | if sdmmc.sta.read().ccrcfail().bit_is_set() { 256 | sdmmc.icr.modify(|_, w| w.ccrcfailc().set_bit()); 257 | return Err(Error::SdmmcError { 258 | t: SdmmcErrorType::CmdCrcFailed, 259 | }); 260 | } 261 | 262 | Ok(()) 263 | } 264 | 265 | pub fn clear_all_static_status_flags(sdmmc: &mut SDMMC1) { 266 | sdmmc.icr.modify(|_, w| { 267 | w.ccrcfailc().set_bit(); 268 | w.dcrcfailc().set_bit(); 269 | w.ctimeoutc().set_bit(); 270 | w.dtimeoutc().set_bit(); 271 | w.txunderrc().set_bit(); 272 | w.rxoverrc().set_bit(); 273 | w.cmdrendc().set_bit(); 274 | w.cmdsentc().set_bit(); 275 | w.dataendc().set_bit(); 276 | w.dbckendc().set_bit(); 277 | w 278 | }); 279 | } 280 | 281 | fn check_for_errors(card_status: u32) -> Result<(), Error> { 282 | if card_status & CardStatusFlags::OCR_ERROR_BITS.bits() == 0 { 283 | Ok(()) 284 | } else if card_status & CardStatusFlags::AKE_SEQ_ERROR.bits() != 0 { 285 | Err(Error::CardError { 286 | t: CardStatusFlags::AKE_SEQ_ERROR, 287 | }) 288 | } else if card_status & CardStatusFlags::ERASE_RESET.bits() != 0 { 289 | Err(Error::CardError { 290 | t: CardStatusFlags::ERASE_RESET, 291 | }) 292 | } else if card_status & CardStatusFlags::CARD_ECC_DISABLED.bits() != 0 { 293 | Err(Error::CardError { 294 | t: CardStatusFlags::CARD_ECC_DISABLED, 295 | }) 296 | } else if card_status & CardStatusFlags::WP_ERASE_SKIP.bits() != 0 { 297 | Err(Error::CardError { 298 | t: CardStatusFlags::WP_ERASE_SKIP, 299 | }) 300 | } else if card_status & CardStatusFlags::CID_CSD_OVERWRITE.bits() != 0 { 301 | Err(Error::CardError { 302 | t: CardStatusFlags::CID_CSD_OVERWRITE, 303 | }) 304 | } else if card_status & CardStatusFlags::CC_ERROR.bits() != 0 { 305 | Err(Error::CardError { 306 | t: CardStatusFlags::CC_ERROR, 307 | }) 308 | } else if card_status & CardStatusFlags::CARD_ECC_FAILED.bits() != 0 { 309 | Err(Error::CardError { 310 | t: CardStatusFlags::CARD_ECC_FAILED, 311 | }) 312 | } else if card_status & CardStatusFlags::ILLEGAL_COMMAND.bits() != 0 { 313 | Err(Error::CardError { 314 | t: CardStatusFlags::ILLEGAL_COMMAND, 315 | }) 316 | } else if card_status & CardStatusFlags::COM_CRC_ERROR.bits() != 0 { 317 | Err(Error::CardError { 318 | t: CardStatusFlags::COM_CRC_ERROR, 319 | }) 320 | } else if card_status & CardStatusFlags::LOCK_UNLOCK_FAILED.bits() != 0 { 321 | Err(Error::CardError { 322 | t: CardStatusFlags::LOCK_UNLOCK_FAILED, 323 | }) 324 | } else if card_status & CardStatusFlags::WP_VIOLATION.bits() != 0 { 325 | Err(Error::CardError { 326 | t: CardStatusFlags::WP_VIOLATION, 327 | }) 328 | } else if card_status & CardStatusFlags::ERASE_PARAM.bits() != 0 { 329 | Err(Error::CardError { 330 | t: CardStatusFlags::ERASE_PARAM, 331 | }) 332 | } else if card_status & CardStatusFlags::ERASE_SEQ_ERROR.bits() != 0 { 333 | Err(Error::CardError { 334 | t: CardStatusFlags::ERASE_SEQ_ERROR, 335 | }) 336 | } else if card_status & CardStatusFlags::BLOCK_LEN_ERROR.bits() != 0 { 337 | Err(Error::CardError { 338 | t: CardStatusFlags::BLOCK_LEN_ERROR, 339 | }) 340 | } else if card_status & CardStatusFlags::ADDRESS_MISALIGNED.bits() != 0 { 341 | Err(Error::CardError { 342 | t: CardStatusFlags::ADDRESS_MISALIGNED, 343 | }) 344 | } else if card_status & CardStatusFlags::ADDRESS_OUT_OF_RANGE.bits() != 0 { 345 | Err(Error::CardError { 346 | t: CardStatusFlags::ADDRESS_OUT_OF_RANGE, 347 | }) 348 | } else { 349 | Err(Error::CardError { 350 | t: CardStatusFlags::ERROR, 351 | }) 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/system_clock.rs: -------------------------------------------------------------------------------- 1 | //! Provides initialization and time-keeping functions for the system clock (`systick`). 2 | 3 | use core::convert::TryFrom; 4 | use core::sync::atomic::{AtomicUsize, Ordering}; 5 | use stm32f7::stm32f7x6::{RCC, SYST}; 6 | 7 | static TICKS: AtomicUsize = AtomicUsize::new(0); 8 | static SYSTEM_CLOCK_SPEED: AtomicUsize = AtomicUsize::new(0); 9 | static FREQUENCY: AtomicUsize = AtomicUsize::new(0); 10 | 11 | /// Increases the global tick count by 1. 12 | pub fn tick() { 13 | TICKS.fetch_add(1, Ordering::AcqRel); 14 | } 15 | 16 | /// Returns the current global tick count. 17 | pub fn ticks() -> usize { 18 | TICKS.load(Ordering::Acquire) 19 | } 20 | 21 | /// Returns the elapsed milliseconds since [`tick()`] was first called. 22 | /// 23 | /// [`tick()`]: self::tick 24 | pub fn ms() -> usize { 25 | ticks_to_ms(ticks()) 26 | } 27 | 28 | /// Wait for the specified number of ticks. 29 | /// 30 | /// This function spins the thread in a while loop until the [`tick()`] function was invoked 31 | /// `ticks` times. 32 | pub fn wait_ticks(ticks: usize) { 33 | let current = self::ticks(); 34 | let desired = current + ticks; 35 | while self::ticks() != desired {} 36 | } 37 | 38 | /// Wait for the specified number of milliseconds. 39 | /// 40 | /// This function spins the thread in a while loop until the specified number of milliseconds 41 | /// have passed. This function is based on [`wait_ticks`] and [`ms_to_ticks`]. 42 | /// 43 | /// [`wait_ticks`]: self::wait_ticks 44 | /// [`ms_to_ticks`]: self::ms_to_ticks 45 | pub fn wait_ms(ms: usize) { 46 | wait_ticks(ms_to_ticks(ms)); 47 | } 48 | 49 | /// Initializes the system clock (systick) of the stm32f7-discovery board to the specified 50 | /// frequency. 51 | /// 52 | /// After calling this function, the interrupt handler for the systick interrupt should call 53 | /// [`tick()`] on each invocation to update the global tick counter in this module. 54 | pub fn init(Hz(frequency): Hz, systick: &mut SYST, rcc: &RCC) { 55 | use cortex_m::peripheral::syst::SystClkSource; 56 | use stm32f7::stm32f7x6::rcc::pllcfgr::PLLPR; 57 | 58 | let pll_cfgr = rcc.pllcfgr.read(); 59 | let pllm = u64::from(pll_cfgr.pllm().bits()); 60 | let plln = u64::from(pll_cfgr.plln().bits()); 61 | let pllp = match pll_cfgr.pllp() { 62 | PLLPR::DIV2 => 2, 63 | PLLPR::DIV4 => 4, 64 | PLLPR::DIV6 => 6, 65 | PLLPR::DIV8 => 8, 66 | }; 67 | 68 | let system_clock_speed = (((25 * 1000 * 1000) / pllm) * plln) / pllp; // HSE runs at 25 MHz 69 | let reload_ticks = u32::try_from(system_clock_speed / frequency as u64).unwrap(); 70 | assert!( 71 | reload_ticks < 0x0100_0000, 72 | "Systick frequency is too low for the SysTick RVR register. \ 73 | The minimum frequency for the current system frequency is {}Hz", 74 | system_clock_speed as f32 / 0x0100_0000 as f32 75 | ); 76 | 77 | SYSTEM_CLOCK_SPEED.store(system_clock_speed as usize, Ordering::Release); 78 | FREQUENCY.store(frequency, Ordering::Release); 79 | 80 | // SysTick Reload Value Register = ((25000/25) * 432) / 2 - 1 = 215_999 81 | // => SysTick interrupt tiggers every 1 ms 82 | systick.set_clock_source(SystClkSource::Core); 83 | systick.set_reload(reload_ticks - 1); 84 | systick.clear_current(); 85 | systick.enable_counter(); 86 | } 87 | 88 | /// Returns the frequency of the system clock. 89 | /// 90 | /// This is the frequency that was passed to [`init`]. 91 | /// 92 | /// [`init`]: self::init 93 | pub fn system_clock_speed() -> Hz { 94 | Hz(SYSTEM_CLOCK_SPEED.load(Ordering::Acquire)) 95 | } 96 | 97 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 98 | #[repr(transparent)] 99 | /// A frequency in Hz. 100 | pub struct Hz(pub usize); 101 | 102 | /// Translates the passed tick number to a number of milliseconds. 103 | /// 104 | /// Depends on the [`system_clock_speed`](self::system_clock_speed). 105 | pub fn ticks_to_ms(ticks: usize) -> usize { 106 | let frequency = FREQUENCY.load(Ordering::Acquire); 107 | (ticks * 1000) / frequency 108 | } 109 | 110 | /// Translates the passed number of milliseconds to a number of ticks. 111 | /// 112 | /// Depends on the [`system_clock_speed`](self::system_clock_speed). 113 | pub fn ms_to_ticks(ms: usize) -> usize { 114 | let frequency = FREQUENCY.load(Ordering::Acquire); 115 | let ticks_x1000 = frequency * ms; 116 | if ticks_x1000 % 1000 == 0 { 117 | ticks_x1000 / 1000 118 | } else { 119 | (ticks_x1000 / 1000) + 1 // round up 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/task_runtime.rs: -------------------------------------------------------------------------------- 1 | //! An experimental runtime for an async-await style task system. 2 | 3 | use crate::mpsc_queue::{PopResult, Queue}; 4 | use alloc::{ 5 | collections::BTreeMap, 6 | prelude::v1::*, 7 | sync::Arc, 8 | }; 9 | use core::ops::{Add, AddAssign}; 10 | use core::pin::Pin; 11 | use futures::{ 12 | future::{FutureObj, LocalFutureObj}, 13 | prelude::*, 14 | task::{LocalSpawn, Poll, Spawn, SpawnError, Waker, RawWaker, RawWakerVTable, Context}, 15 | }; 16 | 17 | pub mod mpsc; 18 | 19 | /// An executor that schedules tasks round-robin, and executes an idle_task 20 | /// if no task is ready to execute. 21 | pub struct Executor { 22 | tasks: BTreeMap>>>, 23 | woken_tasks: Arc>, 24 | next_task_id: TaskId, 25 | idle_task: Option>>>, 26 | } 27 | 28 | impl Spawn for Executor { 29 | fn spawn_obj(&mut self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { 30 | self.spawn_local_obj(future.into()) 31 | } 32 | } 33 | 34 | impl LocalSpawn for Executor { 35 | fn spawn_local_obj(&mut self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { 36 | self.add_task(Box::pin(future)); 37 | Ok(()) 38 | } 39 | } 40 | 41 | impl Executor { 42 | /// Creates a new executor. 43 | pub fn new() -> Self { 44 | Executor { 45 | tasks: BTreeMap::new(), 46 | woken_tasks: Arc::new(Queue::new()), 47 | next_task_id: TaskId(0), 48 | idle_task: None, 49 | } 50 | } 51 | 52 | fn add_task(&mut self, task: Pin>>) { 53 | let id = self.next_task_id; 54 | self.next_task_id += 1; 55 | self.tasks.insert(id, task); 56 | self.woken_tasks.push(id); 57 | } 58 | 59 | /// Sets the specified task as idle task. 60 | /// 61 | /// It will be polled whenever there is no ready-to-run task in the queue. 62 | pub fn set_idle_task(&mut self, future: Fut) 63 | where 64 | Fut: Future + 'static, 65 | { 66 | let future_obj = Box::pin(LocalFutureObj::new(Box::new(future))); 67 | self.idle_task = Some(future_obj); 68 | } 69 | 70 | /// Poll all tasks that are ready to run, until no ready tasks exist. Then poll the idle task 71 | /// once and return. 72 | pub fn run(&mut self) { 73 | match self.woken_tasks.pop() { 74 | PopResult::Data(task_id) => { 75 | let waker = MyWaker { 76 | task_id, 77 | woken_tasks: self.woken_tasks.clone(), 78 | }; 79 | let poll_result = { 80 | let task = self.tasks.get_mut(&task_id).unwrap_or_else(|| panic!("task with id {:?} not found", task_id)); 81 | task.as_mut().poll(&mut Context::from_waker(&waker.into_waker())) 82 | }; 83 | if poll_result.is_ready() { 84 | self.tasks.remove(&task_id).unwrap_or_else(|| panic!("Task {:?} not found", task_id)); 85 | } 86 | } 87 | PopResult::Empty => {} 88 | PopResult::Inconsistent => {} // println!("woken_tasks queue is inconsistent"), 89 | } 90 | if let Some(ref mut idle_task) = self.idle_task { 91 | let _ = idle_task 92 | .as_mut() 93 | .poll(&mut Context::from_waker(&NoOpWaker.into_waker())); 94 | }; 95 | } 96 | } 97 | 98 | #[derive(Clone)] 99 | struct MyWaker { 100 | task_id: TaskId, 101 | woken_tasks: Arc>, 102 | } 103 | 104 | const MY_WAKER_VTABLE: RawWakerVTable = unsafe { RawWakerVTable::new( 105 | core::mem::transmute(MyWaker::waker_drop as fn(Box)), 106 | core::mem::transmute(MyWaker::wake as fn(&MyWaker)), 107 | core::mem::transmute(MyWaker::wake_by_ref as fn(&MyWaker)), 108 | core::mem::transmute(MyWaker::waker_clone as fn(&MyWaker) -> RawWaker), 109 | )}; 110 | 111 | impl MyWaker { 112 | fn into_raw_waker(self) -> RawWaker { 113 | RawWaker::new(Box::into_raw(Box::new(self)) as *const (), &MY_WAKER_VTABLE) 114 | } 115 | fn waker_drop(_: Box) {} 116 | fn waker_clone(&self) -> RawWaker { 117 | self.clone().into_raw_waker() 118 | } 119 | fn wake(&self) { 120 | self.woken_tasks.push(self.task_id); 121 | } 122 | fn wake_by_ref(&self) { 123 | self.woken_tasks.push(self.task_id); 124 | } 125 | fn into_waker(self) -> Waker { 126 | unsafe { 127 | Waker::from_raw(self.into_raw_waker()) 128 | } 129 | } 130 | } 131 | 132 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 133 | struct TaskId(u64); 134 | 135 | impl Add for TaskId { 136 | type Output = TaskId; 137 | 138 | fn add(self, other: u64) -> TaskId { 139 | TaskId(self.0 + other) 140 | } 141 | } 142 | 143 | impl AddAssign for TaskId { 144 | fn add_assign(&mut self, other: u64) { 145 | self.0 += other; 146 | } 147 | } 148 | 149 | struct NoOpWaker; 150 | 151 | impl NoOpWaker { 152 | fn into_waker(self) -> Waker { 153 | unsafe { 154 | Waker::from_raw(self.into_raw_waker()) 155 | } 156 | } 157 | fn into_raw_waker(self) -> RawWaker { 158 | RawWaker::new( 159 | &NoOpWaker as *const _ as *const (), 160 | &RawWakerVTable::new( 161 | (|_| NoOpWaker.into_raw_waker()) as fn(*const ()) -> RawWaker, 162 | (|_| {}) as fn(*const ()), 163 | (|_| {}) as fn(*const ()), 164 | (|_| {}) as fn(*const ()), 165 | ), 166 | ) 167 | } 168 | } 169 | 170 | /// This stream can be used by tasks that want to run when the CPU is idle. 171 | /// 172 | /// It works by alternately returning `Poll::Ready` and `Poll::Pending` from `poll_next`, starting 173 | /// with `Poll::Pending`. When returning `Poll::Pending` it sends the Waker to the 174 | /// `idle_waker_sink` (passed on construction). The idle task polls the other end of this sink and 175 | /// wakes all received tasks when it runs. 176 | // TODO is the behavior correct? 177 | #[derive(Debug, Clone)] 178 | pub struct IdleStream { 179 | idle: bool, 180 | idle_waker_sink: mpsc::UnboundedSender, 181 | } 182 | 183 | impl IdleStream { 184 | /// Creates a new IdleStream with the passed sending end of an idle stream. 185 | /// 186 | /// The idle task should wake the tasks received from the receiving end 187 | /// of the idle stream, thereby waking the tasks on idle. 188 | pub const fn new(idle_waker_sink: mpsc::UnboundedSender) -> Self { 189 | IdleStream { 190 | idle_waker_sink, 191 | idle: false, 192 | } 193 | } 194 | } 195 | 196 | impl futures::prelude::Stream for IdleStream { 197 | type Item = (); 198 | 199 | fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { 200 | let result = if self.idle { 201 | Poll::Ready(Some(())) 202 | } else { 203 | self.idle_waker_sink 204 | .unbounded_send(ctx.waker().clone()) 205 | .expect("sending on idle channel failed"); 206 | Poll::Pending 207 | }; 208 | self.idle = !self.idle; 209 | result 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/task_runtime/mpsc/queue.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. 2 | * Redistribution and use in source and binary forms, with or without 3 | * modification, are permitted provided that the following conditions are met: 4 | * 5 | * 1. Redistributions of source code must retain the above copyright notice, 6 | * this list of conditions and the following disclaimer. 7 | * 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 12 | * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED 13 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 14 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 15 | * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 16 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 17 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 20 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | * 23 | * The views and conclusions contained in the software and documentation are 24 | * those of the authors and should not be interpreted as representing official 25 | * policies, either expressed or implied, of Dmitry Vyukov. 26 | */ 27 | 28 | //! A mostly lock-free multi-producer, single consumer queue for sending 29 | //! messages between asynchronous tasks. 30 | //! 31 | //! The queue implementation is essentially the same one used for mpsc channels 32 | //! in the standard library. 33 | //! 34 | //! Note that the current implementation of this queue has a caveat of the `pop` 35 | //! method, and see the method for more information about it. Due to this 36 | //! caveat, this queue may not be appropriate for all use-cases. 37 | 38 | // http://www.1024cores.net/home/lock-free-algorithms 39 | // /queues/non-intrusive-mpsc-node-based-queue 40 | 41 | // NOTE: this implementation is lifted from the standard library and only 42 | // slightly modified 43 | 44 | pub use self::PopResult::*; 45 | 46 | #[cfg(feature = "std")] 47 | use std::thread; 48 | use alloc::boxed::Box; 49 | use core::cell::UnsafeCell; 50 | use core::ptr; 51 | use core::sync::atomic::{AtomicPtr, Ordering}; 52 | 53 | /// A result of the `pop` function. 54 | pub enum PopResult { 55 | /// Some data has been popped 56 | Data(T), 57 | /// The queue is empty 58 | Empty, 59 | /// The queue is in an inconsistent state. Popping data should succeed, but 60 | /// some pushers have yet to make enough progress in order allow a pop to 61 | /// succeed. It is recommended that a pop() occur "in the near future" in 62 | /// order to see if the sender has made progress or not 63 | Inconsistent, 64 | } 65 | 66 | #[derive(Debug)] 67 | struct Node { 68 | next: AtomicPtr>, 69 | value: Option, 70 | } 71 | 72 | /// The multi-producer single-consumer structure. This is not cloneable, but it 73 | /// may be safely shared so long as it is guaranteed that there is only one 74 | /// popper at a time (many pushers are allowed). 75 | #[derive(Debug)] 76 | pub struct Queue { 77 | head: AtomicPtr>, 78 | tail: UnsafeCell<*mut Node>, 79 | } 80 | 81 | unsafe impl Send for Queue { } 82 | unsafe impl Sync for Queue { } 83 | 84 | impl Node { 85 | unsafe fn new(v: Option) -> *mut Node { 86 | Box::into_raw(Box::new(Node { 87 | next: AtomicPtr::new(ptr::null_mut()), 88 | value: v, 89 | })) 90 | } 91 | } 92 | 93 | impl Queue { 94 | /// Creates a new queue that is safe to share among multiple producers and 95 | /// one consumer. 96 | pub fn new() -> Queue { 97 | let stub = unsafe { Node::new(None) }; 98 | Queue { 99 | head: AtomicPtr::new(stub), 100 | tail: UnsafeCell::new(stub), 101 | } 102 | } 103 | 104 | /// Pushes a new value onto this queue. 105 | pub fn push(&self, t: T) { 106 | unsafe { 107 | let n = Node::new(Some(t)); 108 | let prev = self.head.swap(n, Ordering::AcqRel); 109 | (*prev).next.store(n, Ordering::Release); 110 | } 111 | } 112 | 113 | /// Pops some data from this queue. 114 | /// 115 | /// Note that the current implementation means that this function cannot 116 | /// return `Option`. It is possible for this queue to be in an 117 | /// inconsistent state where many pushes have succeeded and completely 118 | /// finished, but pops cannot return `Some(t)`. This inconsistent state 119 | /// happens when a pusher is preempted at an inopportune moment. 120 | /// 121 | /// This inconsistent state means that this queue does indeed have data, but 122 | /// it does not currently have access to it at this time. 123 | /// 124 | /// This function is unsafe because only one thread can call it at a time. 125 | pub unsafe fn pop(&self) -> PopResult { 126 | let tail = *self.tail.get(); 127 | let next = (*tail).next.load(Ordering::Acquire); 128 | 129 | if !next.is_null() { 130 | *self.tail.get() = next; 131 | assert!((*tail).value.is_none()); 132 | assert!((*next).value.is_some()); 133 | let ret = (*next).value.take().unwrap(); 134 | drop(Box::from_raw(tail)); 135 | return Data(ret); 136 | } 137 | 138 | if self.head.load(Ordering::Acquire) == tail {Empty} else {Inconsistent} 139 | } 140 | 141 | /// Pop an element similarly to `pop` function, but spin-wait on inconsistent 142 | /// queue state instead of returning `Inconsistent`. 143 | /// 144 | /// This function is unsafe because only one thread can call it at a time. 145 | pub unsafe fn pop_spin(&self) -> Option { 146 | loop { 147 | match self.pop() { 148 | Empty => return None, 149 | Data(t) => return Some(t), 150 | // Inconsistent means that there will be a message to pop 151 | // in a short time. This branch can only be reached if 152 | // values are being produced from another thread, so there 153 | // are a few ways that we can deal with this: 154 | // 155 | // 1) Spin 156 | // 2) thread::yield_now() 157 | // 3) task::current().unwrap() & return Pending 158 | // 159 | // For now, thread::yield_now() is used, but it would 160 | // probably be better to spin a few times then yield. 161 | #[cfg(feature = "std")] 162 | Inconsistent => { 163 | thread::yield_now(); 164 | } 165 | #[cfg(not(feature = "std"))] 166 | Inconsistent => { 167 | core::sync::atomic::spin_loop_hint(); 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | impl Drop for Queue { 175 | fn drop(&mut self) { 176 | unsafe { 177 | let mut cur = *self.tail.get(); 178 | while !cur.is_null() { 179 | let next = (*cur).next.load(Ordering::Relaxed); 180 | drop(Box::from_raw(cur)); 181 | cur = next; 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/touch.rs: -------------------------------------------------------------------------------- 1 | //! Touchscreen functions. 2 | 3 | use crate::i2c::{self, I2C}; 4 | use arrayvec::ArrayVec; 5 | use stm32f7::stm32f7x6 as device; 6 | 7 | const FT5336_ADDRESS: i2c::Address = i2c::Address::bits_7(0b011_1000); 8 | const FT5336_FAMILY_ID_REGISTER: u8 = 0xA8; 9 | const FT5336_STATUS_REGISTER: u8 = 0x02; 10 | 11 | // Start locations for reading pressed touches 12 | const FT5336_DATA_REGISTERS: [u8; 5] = [0x03, 0x09, 0x0F, 0x15, 0x1B]; 13 | 14 | /// Checks the whether the device familiy ID register contains the expected value. 15 | pub fn check_family_id(i2c_3: &mut I2C) -> Result<(), i2c::Error> { 16 | i2c_3.connect::(FT5336_ADDRESS, |mut conn| { 17 | // read and check device family ID 18 | assert_eq!(conn.read(FT5336_FAMILY_ID_REGISTER).ok(), Some(0x51)); 19 | Ok(()) 20 | }) 21 | } 22 | 23 | #[derive(Debug, Clone, Copy)] 24 | /// Represents a touch point on the display at coordinates (x,y). 25 | pub struct Touch { 26 | /// The x coordinate of the touch point (horizontal). 27 | pub x: u16, 28 | /// The y coordinate of the touch point (vertical). 29 | pub y: u16, 30 | } 31 | 32 | /// Returns a list of active touch points. 33 | pub fn touches(i2c_3: &mut I2C) -> Result, i2c::Error> { 34 | let mut touches = ArrayVec::new(); 35 | i2c_3.connect::(FT5336_ADDRESS, |mut conn| { 36 | let status = conn.read(FT5336_STATUS_REGISTER)?; 37 | let mut number_of_touches = status & 0x0F; 38 | if number_of_touches > 5 { 39 | number_of_touches = 0; 40 | } 41 | 42 | for &data_reg in FT5336_DATA_REGISTERS.iter().take(number_of_touches.into()) { 43 | let mut touch_data: [u8; 4] = [0; 4]; 44 | conn.read_bytes(data_reg, &mut touch_data)?; 45 | let y = (u16::from(touch_data[0] & 0x0F) << 8) | u16::from(touch_data[1]); 46 | let x = (u16::from(touch_data[2] & 0x0F) << 8) | u16::from(touch_data[3]); 47 | touches.push(Touch { x, y }); 48 | } 49 | Ok(()) 50 | })?; 51 | 52 | Ok(touches) 53 | } 54 | -------------------------------------------------------------------------------- /stlink-1.3.1-win32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/embed-rs/stm32f7-discovery/00d28d4611ba1d7164e03ee7232290477758800a/stlink-1.3.1-win32.zip --------------------------------------------------------------------------------