├── .cargo └── config ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode ├── README.md ├── extensions.json ├── launch.json └── tasks.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── examples ├── accel_pwm.rs ├── blinky.rs ├── blinky_int.rs ├── button.rs ├── button_int.rs ├── compass.rs ├── leds.rs ├── leds_directions.rs └── leds_iterator_test.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src ├── button ├── interrupt.rs └── mod.rs ├── compass.rs ├── leds.rs └── lib.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 2 | # uncomment ONE of these three option to make `cargo run` start a GDB session 3 | # which option to pick depends on your system 4 | # runner = "arm-none-eabi-gdb -q -x openocd.gdb" 5 | # runner = "gdb-multiarch -q -x openocd.gdb" 6 | # runner = "gdb -q -x openocd.gdb" 7 | # runner = "probe-run --chip STM32F303VCTx --connect-under-reset" 8 | 9 | rustflags = [ 10 | # LLD (shipped with the Rust toolchain) is used as the default linker 11 | "-C", "link-arg=-Tlink.x", 12 | 13 | # if you run into problems with LLD switch to the GNU linker by commenting out 14 | # this line 15 | # "-C", "linker=arm-none-eabi-ld", 16 | 17 | # if you need to link to pre-compiled C libraries provided by a C toolchain 18 | # use GCC as the linker by commenting out both lines above and then 19 | # uncommenting the three lines below 20 | # "-C", "linker=arm-none-eabi-gcc", 21 | # "-C", "link-arg=-Wl,-Tlink.x", 22 | # "-C", "link-arg=-nostartfiles", 23 | ] 24 | 25 | [build] 26 | # comment out the following line if you intend to run unit tests on host machine 27 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 28 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | rust: 18 | - stable 19 | - 1.51.0 20 | steps: 21 | - uses: actions/checkout@v1 22 | - name: Install ARM toolchain 23 | run: rustup target add thumbv7em-none-eabihf 24 | - name: Build lib 25 | run: cargo build --verbose 26 | - name: Build examples 27 | run: cargo build --examples --verbose 28 | #- name: Run tests 29 | # run: cargo test --verbose 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ 6 | 7 | # editor files 8 | .vscode/* 9 | !.vscode/*.md 10 | !.vscode/*.svd 11 | !.vscode/launch.json 12 | !.vscode/tasks.json 13 | !.vscode/extensions.json 14 | 15 | #temporary files 16 | .DS_Store 17 | *.swp 18 | thumbs.db -------------------------------------------------------------------------------- /.vscode/README.md: -------------------------------------------------------------------------------- 1 | # VS Code Configuration 2 | 3 | Example configuration for debugging programs in-editor with VS Code. 4 | 5 | ## Required Extensions 6 | 7 | If you have the `code` command in your path, you can run the following commands to install the necessary extensions. 8 | 9 | ```sh 10 | code --install-extension rust-lang.rust 11 | code --install-extension marus25.cortex-debug 12 | ``` 13 | 14 | Otherwise, you can use the Extensions view to search for and install them, or go directly to their marketplace pages and click the "Install" button. 15 | 16 | - [Rust Language Server (RLS)](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust) 17 | - [Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) 18 | 19 | ## Use 20 | 21 | _Note: When you open the project in the editor, you must open an `*.rs` file to trigger the Rust Language Server. 22 | Failure to do so will cause a failure to find the `build` task._ 23 | 24 | The `Debug (OpenOCD)`: Starts a debug session for a `STM32F3DISCOVERY` board. 25 | It will use the default `.cargo/config` configuration to build the executable, upload it to the device, and start a debug session. 26 | 27 | `ITM` output, if used, will be written to the Output view `SWO: ITM [port: 0, type: console]` output. 28 | 29 | ### Git 30 | 31 | Files in the `.vscode/` directory are `.gitignore`d by default because many files that may end up in the `.vscode/` directory should not be committed and shared. 32 | However, a number of files are explicitly tracked, because they define complex debug configurations and should be shared with anyone cloning your project. 33 | 34 | ### SVD File 35 | 36 | The SVD file is a standard way of describing all registers and peripherals of an ARM Cortex-M mCU. 37 | Cortex-Debug needs this file to display the current register values for the peripherals on the device. 38 | 39 | For licensing reasons, we're unable to include the SVD file in the quickstart repository, but it can be downloaded from the [ST's Website][stm32f3]. 40 | 41 | Download the [stm32f3 SVD pack][stm32f3-svd], and copy the `STM32F303.svd` file into `~/.svd/`. 42 | This line of the config tells the Cortex-Debug plug in where to find the file. 43 | 44 | ```json 45 | "svdFile": "${env:HOME}/.svd/STM32F303.svd", 46 | ``` 47 | 48 | Personally, I like keeping them in my home directory so I don't have to keep multiple copies of the SVD on disk, but you could also keep the file under `.vscode/` if you have a private project where there are no licensing concerns around the SVD file. 49 | 50 | ```json 51 | "svdFile": "${workspaceRoot}/.vscode/STM32F303.svd", 52 | ``` 53 | 54 | ### CPU Frequency 55 | 56 | If your device is running at a frequency other than the default 8MHz, you'll need to modify this line of `launch.json` for the `ITM` output to work correctly. 57 | 58 | ```json 59 | "cpuFrequency": 8000000, 60 | ``` 61 | 62 | 63 | 64 | [cortex-debug]: https://github.com/Marus/cortex-debug 65 | [stm32f3]: https://www.st.com/content/st_com/en/products/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus/stm32-mainstream-mcus/stm32f3-series.html#resource 66 | [stm32f3-svd]: https://www.st.com/resource/en/svd/stm32f3_svd.zip 67 | [openocd-config]: http://openocd.org/doc/html/Config-File-Guidelines.html 68 | [openocd-repo]: https://sourceforge.net/p/openocd/code/ci/master/tree/tcl/ 69 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "rust-lang.rust", 8 | "marus25.cortex-debug", 9 | ], 10 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 11 | "unwantedRecommendations": [ 12 | 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | * Requires the Rust Language Server (RLS) and Cortex-Debug extensions 4 | * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust 5 | * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug 6 | */ 7 | "version": "0.2.0", 8 | "configurations": [ 9 | { 10 | /* Launches debug session for currently open example */ 11 | "type": "cortex-debug", 12 | "request": "launch", 13 | "name": "Debug Example", 14 | "servertype": "openocd", 15 | "cwd": "${workspaceRoot}", 16 | "preLaunchTask": "cargo build --examples", 17 | "runToMain": true, 18 | "executable": "./target/thumbv7em-none-eabihf/debug/examples/${fileBasenameNoExtension}", 19 | "preLaunchCommands": ["break rust_begin_unwind"], 20 | "device": "STM32F303VCT6", 21 | "configFiles": [ 22 | "interface/stlink-v2-1.cfg", 23 | "target/stm32f3x.cfg" 24 | ], 25 | "svdFile": "${env:HOME}/.svd/STM32F303.svd", 26 | "swoConfig": { 27 | "enabled": true, 28 | "cpuFrequency": 8000000, 29 | "swoFrequency": 2000000, 30 | "source": "probe", 31 | "decoders": [ 32 | { "type": "console", "label": "ITM", "port": 0 } 33 | ] 34 | } 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | /* 8 | * This is the default cargo build task, 9 | * but we need to provide a label for it, 10 | * so we can invoke it from the debug launcher. 11 | */ 12 | "label": "cargo build", 13 | "type": "cargo", 14 | "subcommand": "build", 15 | "problemMatcher": [ 16 | "$rustc" 17 | ], 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | } 22 | }, 23 | { 24 | "label": "cargo build --release", 25 | "type": "process", 26 | "command": "cargo", 27 | "args": ["build", "--release"], 28 | "problemMatcher": [ 29 | "$rustc" 30 | ], 31 | "group": "build" 32 | }, 33 | { 34 | "label": "cargo build --examples", 35 | "type": "process", 36 | "command": "cargo", 37 | "args": ["build","--examples"], 38 | "problemMatcher": [ 39 | "$rustc" 40 | ], 41 | "group": "build" 42 | }, 43 | { 44 | "label": "cargo build --examples --release", 45 | "type": "process", 46 | "command": "cargo", 47 | "args": ["build","--examples", "--release"], 48 | "problemMatcher": [ 49 | "$rustc" 50 | ], 51 | "group": "build" 52 | }, 53 | { 54 | "label": "cargo clean", 55 | "type": "cargo", 56 | "subcommand": "clean", 57 | "group": "build" 58 | }, 59 | ] 60 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Christopher J. McClellan "] 3 | edition = "2018" 4 | readme = "README.md" 5 | name = "stm32f3-discovery" 6 | description = "Board support package for the STM32F3DISCOVERY board" 7 | repository = "https://github.com/rubberduck203/stm32f3-discovery" 8 | documentation = "https://docs.rs/crate/stm32f3-discovery" 9 | categories = ["embedded", "hardware-support", "no-std"] 10 | keywords = ["discovery", "stm32f3", "bsp", "arm"] 11 | license = "MIT OR Apache-2.0" 12 | version = "0.7.2" 13 | exclude = [ 14 | ".vscode/*", 15 | ] 16 | 17 | [badges] 18 | maintenance = { status = "actively-developed" } 19 | 20 | [package.metadata.docs.rs] 21 | default-target = "thumbv7em-none-eabihf" 22 | targets = [] # build only default target for docs 23 | 24 | [dependencies] 25 | cortex-m = "0.7.2" 26 | cortex-m-rt = "0.6.14" 27 | switch-hal = "0.4.0" 28 | # switch-hal = { git = "https://github.com/rubberduck203/switch-hal", branch = "master" } 29 | lsm303dlhc = "0.2.0" 30 | accelerometer = "0.12.0" 31 | 32 | # For the stm32f303vc mcu 33 | [dependencies.stm32f3xx-hal] 34 | features = ["stm32f303xc", "rt"] 35 | version = "0.8.0" 36 | 37 | [dev-dependencies] 38 | panic-halt = "0.2.0" 39 | panic-itm = "0.4.2" 40 | 41 | # # this lets you use `cargo fix`! 42 | # [[bin]] 43 | # name = "stm32f3-discovery" 44 | # test = false 45 | # bench = false 46 | 47 | [profile.release] 48 | codegen-units = 1 # better optimizations 49 | debug = true # symbols are nice and they don't increase the size on Flash 50 | lto = true # better optimizations 51 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Christopher McClellan 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Christopher McClellan 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 | # `stm32f3-discovery` 2 | 3 | Board support package for the [STM32F3DISCOVERY][stm32f3discovery] board. 4 | 5 | ![Rust](https://github.com/rubberduck203/stm32f3-discovery/workflows/Rust/badge.svg) 6 | [![crates.io](https://img.shields.io/crates/d/stm32f3-discovery.svg)](https://crates.io/crates/stm32f3-discovery) 7 | [![crates.io](https://img.shields.io/crates/v/stm32f3-discovery.svg)](https://crates.io/crates/stm32f3-discovery) 8 | [![docs.rs](https://docs.rs/stm32f3-discovery/badge.svg)](https://docs.rs/stm32f3-discovery) 9 | 10 | ## Dependencies 11 | 12 | To build embedded programs using this you'll need: 13 | 14 | - Rust 1.51 or newer toolchain 15 | - `rust-std` components (pre-compiled `core` crate) for the ARM Cortex-M 16 | target. 17 | 18 | ``` console 19 | $ cargo install cargo-generate 20 | $ rustup target add thumbv7em-none-eabihf 21 | ``` 22 | 23 | For more info on working with embedded Rust, see the [Embedded Rust Book][book] and the [Discovery Book][discovery-book]. 24 | 25 | ## Documentation 26 | 27 | https://docs.rs/stm32f3-discovery 28 | 29 | For the board specific functionality this crate adds, see: 30 | - The [examples directory](./examples). 31 | - The [leds module documentation](https://docs.rs/stm32f3-discovery/0.3.4/stm32f3_discovery/leds/index.html) 32 | - The [button module documentation](https://docs.rs/stm32f3-discovery/0.3.4/stm32f3_discovery/button/index.html) 33 | - The [compass module](https://docs.rs/stm32f3-discovery/0.3.4/stm32f3_discovery/compass/index.html) and [lsm303dhlc documentation](https://docs.rs/lsm303dlhc/0.2.0/lsm303dlhc/) 34 | 35 | ## VS Code 36 | 37 | This repository includes launch configurations for debugging CortexM programs with Visual Studio Code in the `.vscode/` directory. 38 | See [.vscode/README.md](./.vscode/README.md) for more information. 39 | 40 | To debug one of the examples, open the example source file in the editor and press F5. 41 | 42 | # License 43 | 44 | This template is licensed under either of 45 | 46 | - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) 47 | - MIT license (http://opensource.org/licenses/MIT) 48 | 49 | at your option. 50 | 51 | ## Contribution 52 | 53 | Unless you explicitly state otherwise, any contribution intentionally submitted 54 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 55 | dual licensed as above, without any additional terms or conditions. 56 | 57 | 58 | [stm32f3discovery]: https://www.st.com/en/evaluation-tools/stm32f3discovery.html# 59 | [book]: https://rust-embedded.github.io/book 60 | [discovery-book]: https://rust-embedded.github.io/discovery/ 61 | 62 | ## Changelog 63 | 64 | ### Unreleased 65 | 66 | - Bump HAL to 0.8.0 for getting back [`MonoTimer`](https://docs.rs/stm32f3xx-hal/0.8.0/stm32f3xx_hal/timer/struct.MonoTimer.html) which was accidentially droped with release 0.6.1 and came back with the latest release. 67 | 68 | ### 0.7.2 69 | 70 | Implements the [Accelerometer trait](https://docs.rs/accelerometer/latest/accelerometer/trait.Accelerometer.html) from the [Accelerometer crate](https://crates.io/crates/accelerometer). 71 | 72 | ### 0.7.1 73 | 74 | - Implements an `Iterator` for the `Leds` struct and introduced the [Leds::iter_mut()](https://docs.rs/stm32f3-discovery/0.7.1/stm32f3_discovery/leds/struct.Leds.html#method.iter_mut) method. 75 | 76 | Testing shows that direct iteration over the leds using `Leds::iter_mut()` can save up to 800 bytes off the size of the final binary over the old `Leds::into_array()` method. 77 | 78 | See: https://github.com/rubberduck203/stm32f3-discovery/pull/41 79 | 80 | - Adds the ability to obtain a mutable reference to a led based on it's compass direction on the board. 81 | 82 | See: [Leds::for_direction()](https://docs.rs/stm32f3-discovery/0.7.1/stm32f3_discovery/leds/struct.Leds.html#method.for_direction) 83 | 84 | Contributed by [Christian Meusel](https://github.com/sirhcel) 85 | 86 | ### 0.7.0 87 | 88 | Updates `stm32f3xx-hal` to 0.7.0. 89 | Since we re-export the `stm32f3xx-hal`, any breaking changes in their API are also breaking changes in ours. 90 | For details see the [stm32f3xx-hal changelog](https://github.com/stm32-rs/stm32f3xx-hal/blob/66c0d21ae19ae0bee09ec834a6c9c90b2191e17d/CHANGELOG.md#breaking-changes) 91 | 92 | 93 | Although the minimum Rust version is technically still 1.49, because of changes to `embedded_time`, the minimum version of Cargo is now 1.51, so we're updating our MSRV to 1.51. 94 | 95 | ### 0.6.1 96 | 97 | Update `cortex-m`, `cortex-m-rt`, and `switch-hal` dependencies. 98 | 99 | ### 0.6.0 100 | 101 | Update `stm32f3xx-hal` version. 102 | `stm32f3xx-hal` had breaking changes. 103 | Since we re-export the HAL, that means we also had breaking changes. 104 | 105 | For details, see the [stm32f3xx-hal changelog](https://github.com/stm32-rs/stm32f3xx-hal/blob/HEAD/CHANGELOG.md#breaking-changes). 106 | 107 | ### 0.5.0 108 | 109 | - Updated dependencies 110 | - `InputSwitch for UserButton` now has an `Error` type of `core::convert::Infallible` instead of `()` 111 | 112 | ### 0.4.0 113 | 114 | - Updated `stm32f3xx-hal` from 0.4.0 to 0.4.1 115 | - Allows setting `TriggerMode` on the user button (breaking change) 116 | - Removes deprecated `GpioE` struct and `Leds::init` function 117 | 118 | ### 0.3.4 119 | 120 | - Introduced `Compass` struct and implemented [Accelerometer trait](https://crates.io/crates/accelerometer). 121 | - Add `Leds::new` function and deprecate `Leds::init`. 122 | 123 | ### 0.3.3 124 | 125 | - Add `wait_for_interrupt` function 126 | - Upgrade `switch-hal` version 127 | 128 | ### 0.3.2 129 | 130 | - Re-export `lsm303dhlc` driver 131 | -------------------------------------------------------------------------------- /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 | // Only re-run the build script when memory.x is changed, 16 | // instead of when any part of the source code changes. 17 | println!("cargo:rerun-if-changed=memory.x"); 18 | } 19 | -------------------------------------------------------------------------------- /examples/accel_pwm.rs: -------------------------------------------------------------------------------- 1 | //! The three LEDs should gradually light up corresponding to the orientation of the board along its three axes 2 | //! (or more precisely they indicate the direction of Earth's gravity relative to the three axes of the board): 3 | //! 4 | //! - Lying down flat the blue LDE should be full on, indicating that gravity is perpendicular to the board. 5 | //! - @hen you slowly turn it around its long axis the green LED should gradually go on while the blue gradually fades out. 6 | //! The green LED should be fully on when the green LED is 'facing down' and the other axes are straight. 7 | //! - Likewise when you turn it along the short axis (from a flat position) the red LED will go on. 8 | 9 | #![deny(unsafe_code)] 10 | #![no_std] 11 | #![no_main] 12 | 13 | extern crate panic_itm; 14 | 15 | use cortex_m_rt::entry; 16 | use stm32f3_discovery::accelerometer::RawAccelerometer; 17 | use stm32f3_discovery::compass::Compass; 18 | use stm32f3_discovery::stm32f3xx_hal; 19 | use stm32f3xx_hal::delay::Delay; 20 | use stm32f3xx_hal::pwm::tim1; 21 | use stm32f3xx_hal::{prelude::*, pac}; 22 | 23 | #[entry] 24 | fn main() -> ! { 25 | let device_periphs = pac::Peripherals::take().unwrap(); 26 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 27 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 28 | // Configure our clocks 29 | let mut flash = device_periphs.FLASH.constrain(); 30 | 31 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 32 | let mut delay = Delay::new(core_periphs.SYST, clocks); 33 | let mut gpiob = device_periphs.GPIOB.split(&mut reset_and_clock_control.ahb); 34 | 35 | // Prep the pins we need in their correct alternate function 36 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 37 | 38 | let led_blue = gpioe.pe8.into_af2_push_pull(&mut gpioe.moder, &mut gpioe.otyper, &mut gpioe.afrh); 39 | let led_green = gpioe.pe11.into_af2_push_pull(&mut gpioe.moder, &mut gpioe.otyper, &mut gpioe.afrh); 40 | let lef_red = gpioe.pe13.into_af2_push_pull(&mut gpioe.moder, &mut gpioe.otyper, &mut gpioe.afrh); 41 | 42 | let max_duty = 4096; 43 | 44 | let tim1_channels = tim1( 45 | device_periphs.TIM1, 46 | max_duty, // resolution of duty cycle 47 | 500.Hz(), // frequency of period 48 | &clocks, // To get the timer's clock speed 49 | ); 50 | 51 | let mut pwm_ch_blue = tim1_channels.0.output_to_pe8(led_blue); 52 | pwm_ch_blue.set_duty(0); 53 | pwm_ch_blue.enable(); 54 | 55 | let mut pwm_ch_green = tim1_channels.1.output_to_pe11(led_green); 56 | pwm_ch_green.set_duty(0); 57 | pwm_ch_green.enable(); 58 | 59 | let mut pwm_ch_red = tim1_channels.2.output_to_pe13(lef_red); 60 | pwm_ch_red.set_duty(0); 61 | pwm_ch_red.enable(); 62 | 63 | let mut compass = Compass::new( 64 | gpiob.pb6, 65 | gpiob.pb7, 66 | &mut gpiob.moder, 67 | &mut gpiob.otyper, 68 | &mut gpiob.afrl, 69 | device_periphs.I2C1, 70 | clocks, 71 | &mut reset_and_clock_control.apb1, 72 | ) 73 | .unwrap(); 74 | 75 | loop { 76 | const SENSITIVITY: f32 = 1. / (1 << 14) as f32; 77 | 78 | let acc = compass.accel_raw().unwrap(); 79 | 80 | let x = f32_abs(f32::from(acc.x) * SENSITIVITY); 81 | let y = f32_abs(f32::from(acc.y) * SENSITIVITY); 82 | let z = f32_abs(f32::from(acc.z) * SENSITIVITY); 83 | 84 | // probably the accel vector should be transformed to angles, but the steeper-than-usual slope 85 | // used in LED_RAMP seems to have the same effect. 86 | 87 | let r = led_ramp(x); 88 | let g = led_ramp(y); 89 | let b = led_ramp(z); 90 | 91 | pwm_ch_blue.set_duty(b); 92 | pwm_ch_green.set_duty(g); 93 | pwm_ch_red.set_duty(r); 94 | delay.delay_ms(100_u16); 95 | } 96 | } 97 | 98 | // FIXME: can we use f32::abs somehow? 99 | fn f32_abs(v: f32) -> f32 { 100 | if v < 0.0 { 101 | -v 102 | } else { 103 | v 104 | } 105 | } 106 | 107 | // inverse power-law ramp adapted from 108 | // https://forum.arduino.cc/index.php?topic=147818.msg1113233#msg1113233 109 | 110 | // float a = 0.25; // usually either 0.5 or 0.33, but 0.25 looks best in this case 111 | // int Pmax = pow(4096,a); 112 | // int N = 255; 113 | // for (int n = 0; n <= N; ++n) 114 | // { 115 | // std::cout << int(pow(Pmax * ((float)n/(float)N), 1/a) + 0.5) << ", "; 116 | // } 117 | // TODO: generate with macro? 118 | static LED_RAMP: [u16; 256] = [ 119 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 120 | 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11, 12, 13, 13, 121 | 14, 15, 16, 17, 18, 20, 21, 22, 23, 25, 26, 28, 29, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 51, 122 | 53, 55, 58, 61, 64, 66, 69, 72, 76, 79, 82, 86, 89, 93, 97, 101, 105, 109, 113, 118, 122, 127, 123 | 132, 137, 142, 147, 152, 158, 164, 169, 175, 182, 188, 194, 201, 208, 215, 222, 229, 237, 244, 124 | 252, 260, 268, 277, 285, 294, 303, 312, 322, 331, 341, 351, 362, 372, 383, 394, 405, 417, 428, 125 | 440, 452, 465, 477, 490, 504, 517, 531, 545, 559, 574, 589, 604, 619, 635, 651, 667, 684, 701, 126 | 718, 736, 753, 772, 790, 809, 828, 848, 868, 888, 909, 930, 951, 972, 995, 1017, 1040, 1063, 127 | 1086, 1110, 1135, 1159, 1185, 1210, 1236, 1262, 1289, 1316, 1344, 1372, 1401, 1430, 1459, 1489, 128 | 1519, 1550, 1581, 1613, 1645, 1678, 1711, 1744, 1779, 1813, 1848, 1884, 1920, 1957, 1994, 2032, 129 | 2070, 2109, 2148, 2188, 2228, 2269, 2311, 2353, 2396, 2439, 2483, 2527, 2572, 2618, 2664, 2711, 130 | 2758, 2806, 2855, 2904, 2954, 3005, 3056, 3108, 3161, 3214, 3268, 3322, 3378, 3434, 3490, 3548, 131 | 3606, 3664, 3724, 3784, 3845, 3907, 3969, 4032, 4096, 132 | ]; 133 | 134 | fn led_ramp(v: f32) -> u16 { 135 | let v = if v < 0.0 { 136 | 0.0 137 | } else if v > 1.0 { 138 | 1.0 139 | } else { 140 | v 141 | }; 142 | LED_RAMP[(v * 255.0) as usize] 143 | } 144 | -------------------------------------------------------------------------------- /examples/blinky.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m_rt::entry; 7 | 8 | use stm32f3_discovery::stm32f3xx_hal::delay::Delay; 9 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 10 | use stm32f3_discovery::stm32f3xx_hal::pac; 11 | 12 | use stm32f3_discovery::leds::Leds; 13 | use stm32f3_discovery::switch_hal::{OutputSwitch, ToggleableOutputSwitch}; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let device_periphs = pac::Peripherals::take().unwrap(); 18 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 19 | 20 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 21 | let mut flash = device_periphs.FLASH.constrain(); 22 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 23 | let mut delay = Delay::new(core_periphs.SYST, clocks); 24 | 25 | // initialize user leds 26 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 27 | let mut leds = Leds::new( 28 | gpioe.pe8, 29 | gpioe.pe9, 30 | gpioe.pe10, 31 | gpioe.pe11, 32 | gpioe.pe12, 33 | gpioe.pe13, 34 | gpioe.pe14, 35 | gpioe.pe15, 36 | &mut gpioe.moder, 37 | &mut gpioe.otyper, 38 | ); 39 | 40 | loop { 41 | leds.ld3.toggle().ok(); 42 | delay.delay_ms(1000u16); 43 | leds.ld3.toggle().ok(); 44 | delay.delay_ms(1000u16); 45 | 46 | //explicit on/off 47 | leds.ld4.on().ok(); 48 | delay.delay_ms(1000u16); 49 | leds.ld4.off().ok(); 50 | delay.delay_ms(1000u16); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/blinky_int.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use core::cell::RefCell; 7 | use core::ops::DerefMut; 8 | 9 | use cortex_m::interrupt::{free, Mutex}; 10 | 11 | use cortex_m_rt::entry; 12 | 13 | use stm32f3xx_hal::prelude::*; 14 | use stm32f3xx_hal::timer::{Event, Timer}; 15 | use stm32f3xx_hal::pac; 16 | 17 | use pac::{interrupt, Interrupt}; 18 | 19 | use switch_hal::{ToggleableOutputSwitch, IntoSwitch}; 20 | 21 | use stm32f3_discovery::wait_for_interrupt; 22 | 23 | 24 | static TIM: Mutex>>> = Mutex::new(RefCell::new(None)); 25 | 26 | #[interrupt] 27 | fn TIM7() { 28 | free(|cs| { 29 | if let Some(ref mut tim7) = TIM.borrow(cs).borrow_mut().deref_mut() { 30 | tim7.clear_event(Event::Update); 31 | } 32 | }); 33 | } 34 | 35 | #[entry] 36 | fn main() -> ! { 37 | let peripherals = stm32f3xx_hal::pac::Peripherals::take().unwrap(); 38 | let mut flash = peripherals.FLASH.constrain(); 39 | let mut rcc = peripherals.RCC.constrain(); 40 | 41 | let clocks = rcc.cfgr.freeze(&mut flash.acr); 42 | let mut timer = Timer::new(peripherals.TIM7, clocks, &mut rcc.apb1); 43 | timer.start(500u32.milliseconds()); 44 | timer.enable_interrupt(Event::Update); 45 | free(|cs| { 46 | TIM.borrow(cs).replace(Some(timer)); 47 | }); 48 | 49 | let mut gpio = peripherals.GPIOE.split(&mut rcc.ahb); 50 | let pin = gpio.pe9.into_push_pull_output(&mut gpio.moder, &mut gpio.otyper); 51 | let mut led = pin.into_active_high_switch(); 52 | 53 | unsafe { 54 | pac::NVIC::unmask(Interrupt::TIM7); 55 | } 56 | 57 | loop { 58 | led.toggle().ok(); 59 | wait_for_interrupt(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/button.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | use cortex_m_rt::entry; 6 | 7 | use stm32f3_discovery::stm32f3xx_hal::delay::Delay; 8 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 9 | use stm32f3_discovery::stm32f3xx_hal::pac; 10 | 11 | use stm32f3_discovery::button::UserButton; 12 | use stm32f3_discovery::leds::Leds; 13 | use stm32f3_discovery::switch_hal::{InputSwitch, OutputSwitch}; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let device_periphs = pac::Peripherals::take().unwrap(); 18 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 19 | 20 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 21 | let mut flash = device_periphs.FLASH.constrain(); 22 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 23 | let mut delay = Delay::new(core_periphs.SYST, clocks); 24 | 25 | // initialize user leds 26 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 27 | let leds = Leds::new( 28 | gpioe.pe8, 29 | gpioe.pe9, 30 | gpioe.pe10, 31 | gpioe.pe11, 32 | gpioe.pe12, 33 | gpioe.pe13, 34 | gpioe.pe14, 35 | gpioe.pe15, 36 | &mut gpioe.moder, 37 | &mut gpioe.otyper, 38 | ); 39 | let mut status_led = leds.ld3; 40 | 41 | // initialize user button 42 | let mut gpioa = device_periphs.GPIOA.split(&mut reset_and_clock_control.ahb); 43 | let button = UserButton::new(gpioa.pa0, &mut gpioa.moder, &mut gpioa.pupdr); 44 | 45 | loop { 46 | delay.delay_ms(50u16); 47 | 48 | match button.is_active() { 49 | Ok(true) => { 50 | status_led.on().ok(); 51 | } 52 | Ok(false) => { 53 | status_led.off().ok(); 54 | } 55 | Err(_) => { 56 | panic!("Failed to read button state"); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/button_int.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m_rt::entry; 7 | 8 | use stm32f3_discovery::stm32f3xx_hal::interrupt; 9 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 10 | use stm32f3_discovery::stm32f3xx_hal::pac; 11 | use stm32f3_discovery::wait_for_interrupt; 12 | 13 | use core::sync::atomic::{AtomicBool, Ordering}; 14 | use stm32f3_discovery::button; 15 | use stm32f3_discovery::button::interrupt::TriggerMode; 16 | 17 | use stm32f3_discovery::leds::Leds; 18 | use stm32f3_discovery::switch_hal::ToggleableOutputSwitch; 19 | 20 | static USER_BUTTON_PRESSED: AtomicBool = AtomicBool::new(false); 21 | 22 | #[interrupt] 23 | fn EXTI0() { 24 | //If we don't clear the interrupt to signal it's been serviced, it will continue to fire. 25 | button::interrupt::clear(); 26 | // pa0 has a low pass filter on it, so no need to debounce in software 27 | USER_BUTTON_PRESSED.store(true, Ordering::Relaxed); 28 | } 29 | 30 | #[entry] 31 | fn main() -> ! { 32 | let device_periphs = pac::Peripherals::take().unwrap(); 33 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 34 | 35 | // initialize user leds 36 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 37 | let leds = Leds::new( 38 | gpioe.pe8, 39 | gpioe.pe9, 40 | gpioe.pe10, 41 | gpioe.pe11, 42 | gpioe.pe12, 43 | gpioe.pe13, 44 | gpioe.pe14, 45 | gpioe.pe15, 46 | &mut gpioe.moder, 47 | &mut gpioe.otyper, 48 | ); 49 | let mut status_led = leds.ld3; 50 | 51 | button::interrupt::enable( 52 | &device_periphs.EXTI, 53 | &device_periphs.SYSCFG, 54 | TriggerMode::Rising, 55 | ); 56 | 57 | loop { 58 | // check to see if flag was active and clear it 59 | if USER_BUTTON_PRESSED.swap(false, Ordering::AcqRel) { 60 | status_led.toggle().ok(); 61 | } 62 | 63 | wait_for_interrupt(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/compass.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m::iprintln; 7 | use cortex_m::peripheral::syst::SystClkSource; 8 | use cortex_m_rt::{entry, exception}; 9 | 10 | use accelerometer::{Accelerometer, RawAccelerometer}; 11 | use stm32f3_discovery::compass::Compass; 12 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 13 | use stm32f3_discovery::stm32f3xx_hal::pac; 14 | use stm32f3_discovery::wait_for_interrupt; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let device_periphs = pac::Peripherals::take().unwrap(); 19 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 20 | 21 | let mut core_periphs = cortex_m::Peripherals::take().unwrap(); 22 | let mut flash = device_periphs.FLASH.constrain(); 23 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 24 | 25 | // setup 1 second systick 26 | let mut syst = core_periphs.SYST; 27 | syst.set_clock_source(SystClkSource::Core); 28 | syst.set_reload(8_000_000); // period = 1s 29 | syst.enable_counter(); 30 | syst.enable_interrupt(); 31 | 32 | // setup ITM output 33 | let stim = &mut core_periphs.ITM.stim[0]; 34 | 35 | let mut gpiob = device_periphs.GPIOB.split(&mut reset_and_clock_control.ahb); 36 | 37 | // new lsm303 driver uses continuous mode, so no need wait for interrupts on DRDY 38 | let mut compass = Compass::new( 39 | gpiob.pb6, 40 | gpiob.pb7, 41 | &mut gpiob.moder, 42 | &mut gpiob.otyper, 43 | &mut gpiob.afrl, 44 | device_periphs.I2C1, 45 | clocks, 46 | &mut reset_and_clock_control.apb1, 47 | ) 48 | .unwrap(); 49 | 50 | loop { 51 | let accel = compass.accel_raw().unwrap(); 52 | iprintln!(stim, "RawAccel:{:?}", accel); 53 | 54 | let normalized_accel = compass.accel_norm().unwrap(); 55 | iprintln!(stim, "G-Force:{:?}", normalized_accel); 56 | 57 | let mag = compass.mag_raw().unwrap(); 58 | iprintln!(stim, "RawMag:{:?}", mag); 59 | 60 | wait_for_interrupt(); 61 | } 62 | } 63 | 64 | #[exception] 65 | fn SysTick() { 66 | // make sure we don't compile away 67 | cortex_m::asm::nop(); 68 | } 69 | -------------------------------------------------------------------------------- /examples/leds.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m_rt::entry; 7 | 8 | use stm32f3_discovery::stm32f3xx_hal::delay::Delay; 9 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 10 | use stm32f3_discovery::stm32f3xx_hal::pac; 11 | 12 | use stm32f3_discovery::leds::Leds; 13 | use stm32f3_discovery::switch_hal::OutputSwitch; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let device_periphs = pac::Peripherals::take().unwrap(); 18 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 19 | 20 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 21 | let mut flash = device_periphs.FLASH.constrain(); 22 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 23 | let mut delay = Delay::new(core_periphs.SYST, clocks); 24 | 25 | // initialize user leds 26 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 27 | let mut leds = Leds::new( 28 | gpioe.pe8, 29 | gpioe.pe9, 30 | gpioe.pe10, 31 | gpioe.pe11, 32 | gpioe.pe12, 33 | gpioe.pe13, 34 | gpioe.pe14, 35 | gpioe.pe15, 36 | &mut gpioe.moder, 37 | &mut gpioe.otyper, 38 | ); 39 | 40 | loop { 41 | let ms_delay = 50u16; 42 | 43 | for led in &mut leds { 44 | led.on().ok(); 45 | delay.delay_ms(ms_delay); 46 | led.off().ok(); 47 | delay.delay_ms(ms_delay); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/leds_directions.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m_rt::entry; 7 | 8 | use stm32f3_discovery::stm32f3xx_hal::delay::Delay; 9 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 10 | use stm32f3_discovery::stm32f3xx_hal::pac; 11 | 12 | use stm32f3xx_hal::gpio::gpioe; 13 | use stm32f3xx_hal::gpio::{Output, PushPull}; 14 | 15 | use stm32f3_discovery::leds::{Direction, Leds}; 16 | use stm32f3_discovery::switch_hal::{Switch, ActiveHigh, OutputSwitch}; 17 | 18 | #[entry] 19 | fn main() -> ! { 20 | let device_periphs = pac::Peripherals::take().unwrap(); 21 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 22 | 23 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 24 | let mut flash = device_periphs.FLASH.constrain(); 25 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 26 | let mut delay = Delay::new(core_periphs.SYST, clocks); 27 | 28 | // initialize user leds 29 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 30 | let mut leds = Leds::new( 31 | gpioe.pe8, 32 | gpioe.pe9, 33 | gpioe.pe10, 34 | gpioe.pe11, 35 | gpioe.pe12, 36 | gpioe.pe13, 37 | gpioe.pe14, 38 | gpioe.pe15, 39 | &mut gpioe.moder, 40 | &mut gpioe.otyper, 41 | ); 42 | 43 | loop { 44 | let fast_delay = 50u16; 45 | for direction in Direction::iter() { 46 | let led = &mut leds.for_direction(*direction); 47 | 48 | led.on().ok(); 49 | delay.delay_ms(fast_delay); 50 | led.off().ok(); 51 | delay.delay_ms(fast_delay); 52 | } 53 | 54 | slow_blink(leds.for_direction(Direction::North), &mut delay); 55 | slow_blink(leds.for_direction(Direction::South), &mut delay); 56 | slow_blink(leds.for_direction(Direction::East), &mut delay); 57 | slow_blink(leds.for_direction(Direction::West), &mut delay); 58 | } 59 | } 60 | 61 | fn slow_blink(switch: &mut Switch>, ActiveHigh>, delay: &mut Delay) { 62 | let slow_delay = 250u16; 63 | switch.on().ok(); 64 | delay.delay_ms(slow_delay); 65 | switch.off().ok(); 66 | delay.delay_ms(slow_delay); 67 | } 68 | -------------------------------------------------------------------------------- /examples/leds_iterator_test.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate panic_itm; 5 | 6 | use cortex_m_rt::entry; 7 | 8 | use stm32f3_discovery::stm32f3xx_hal::delay::Delay; 9 | use stm32f3_discovery::stm32f3xx_hal::prelude::*; 10 | use stm32f3_discovery::stm32f3xx_hal::pac; 11 | 12 | use stm32f3_discovery::leds::Leds; 13 | use stm32f3_discovery::switch_hal::OutputSwitch; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | let device_periphs = pac::Peripherals::take().unwrap(); 18 | let mut reset_and_clock_control = device_periphs.RCC.constrain(); 19 | 20 | let core_periphs = cortex_m::Peripherals::take().unwrap(); 21 | let mut flash = device_periphs.FLASH.constrain(); 22 | let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr); 23 | let mut delay = Delay::new(core_periphs.SYST, clocks); 24 | 25 | // initialize user leds 26 | let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb); 27 | let mut leds = Leds::new( 28 | gpioe.pe8, 29 | gpioe.pe9, 30 | gpioe.pe10, 31 | gpioe.pe11, 32 | gpioe.pe12, 33 | gpioe.pe13, 34 | gpioe.pe14, 35 | gpioe.pe15, 36 | &mut gpioe.moder, 37 | &mut gpioe.otyper, 38 | ); 39 | 40 | loop { 41 | let ms_delay = 50u16; 42 | 43 | // iterate through the leds in reverse 44 | for led in leds.iter_mut().rev() { 45 | led.on().ok(); 46 | delay.delay_ms(ms_delay); 47 | led.off().ok(); 48 | delay.delay_ms(ms_delay); 49 | } 50 | 51 | delay.delay_ms(ms_delay); 52 | 53 | // verify we stop when meeting in the middle 54 | let mut iter = leds.iter_mut(); 55 | iter.next().map(|led| led.on().ok()); 56 | delay.delay_ms(ms_delay); 57 | iter.next_back().map(|led| led.on().ok()); 58 | delay.delay_ms(ms_delay); 59 | iter.next().map(|led| led.on().ok()); 60 | delay.delay_ms(ms_delay); 61 | iter.next_back().map(|led| led.on().ok()); 62 | delay.delay_ms(ms_delay); 63 | iter.next().map(|led| led.on().ok()); 64 | delay.delay_ms(ms_delay); 65 | iter.next_back().map(|led| led.on().ok()); 66 | delay.delay_ms(ms_delay); 67 | iter.next().map(|led| led.on().ok()); 68 | delay.delay_ms(ms_delay); 69 | iter.next_back().map(|led| led.on().ok()); 70 | delay.delay_ms(ms_delay); 71 | // we're in the middle, so panic if either of the next two calls returns a led 72 | iter.next().map(|_| panic!("Got a Some!")); 73 | iter.next_back().map(|_| panic!("Got a Some!")); 74 | 75 | // turn everything back off 76 | for led in &mut leds { 77 | led.off().ok(); 78 | delay.delay_ms(ms_delay); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | /* Linker script for the STM32F303VCT6 */ 2 | MEMORY 3 | { 4 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ 5 | FLASH : ORIGIN = 0x08000000, LENGTH = 256K 6 | RAM : ORIGIN = 0x20000000, LENGTH = 40K 7 | } 8 | 9 | /* This is where the call stack will be allocated. */ 10 | /* The stack is of the full descending type. */ 11 | /* You may want to use this variable to locate the call stack and static 12 | variables in different memory regions. Below is shown the default value */ 13 | /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ 14 | 15 | /* You can use this symbol to customize the location of the .text section */ 16 | /* If omitted the .text section will be placed right after the .vector_table 17 | section */ 18 | /* This is required only on microcontrollers that store some configuration right 19 | after the vector table */ 20 | /* _stext = ORIGIN(FLASH) + 0x400; */ 21 | 22 | /* Example of putting non-initialized variables into custom RAM locations. */ 23 | /* This assumes you have defined a region RAM2 above, and in the Rust 24 | sources added the attribute `#[link_section = ".ram2bss"]` to the data 25 | you want to place there. */ 26 | /* Note that the section will not be zero-initialized by the runtime! */ 27 | /* SECTIONS { 28 | .ram2bss (NOLOAD) : ALIGN(4) { 29 | *(.ram2bss); 30 | . = ALIGN(4); 31 | } > RAM2 32 | } INSERT AFTER .bss; 33 | */ 34 | -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the STM32F3DISCOVERY development board 2 | 3 | # Depending on the hardware revision you got you'll have to pick ONE of these 4 | # interfaces. At any time only one interface should be commented out. 5 | 6 | # Revision C (newer revision) 7 | source [find interface/stlink-v2-1.cfg] 8 | 9 | # Revision A and B (older revisions) 10 | # source [find interface/stlink-v2.cfg] 11 | 12 | source [find target/stm32f3x.cfg] 13 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # set backtrace limit to not have infinite backtrace loops 7 | set backtrace limit 32 8 | 9 | # detect unhandled exceptions, hard faults and panics 10 | break DefaultHandler 11 | break HardFault 12 | break rust_begin_unwind 13 | # # run the next few lines so the panic message is printed immediately 14 | # # the number needs to be adjusted for your panic handler 15 | # commands $bpnum 16 | # next 4 17 | # end 18 | 19 | # *try* to stop at the user entry point (it might be gone due to inlining) 20 | break main 21 | 22 | monitor arm semihosting enable 23 | 24 | # # send captured ITM to the file itm.fifo 25 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 26 | # # 8000000 must match the core clock frequency 27 | # monitor tpiu config internal itm.txt uart off 8000000 28 | 29 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 30 | # # 8000000 must match the core clock frequency 31 | # # 2000000 is the frequency of the SWO pin 32 | # monitor tpiu config external uart off 8000000 2000000 33 | 34 | # # enable ITM port 0 35 | # monitor itm port 0 on 36 | 37 | load 38 | 39 | # start the process but immediately halt the processor 40 | stepi 41 | -------------------------------------------------------------------------------- /src/button/interrupt.rs: -------------------------------------------------------------------------------- 1 | //! Provides interrupt features for `UserButton` on PA0 for the board 2 | use cortex_m::peripheral::NVIC; 3 | use stm32f3xx_hal::pac::exti::{FTSR1, IMR1, RTSR1}; 4 | use stm32f3xx_hal::pac::syscfg::EXTICR1; 5 | use stm32f3xx_hal::pac::{Interrupt, EXTI, SYSCFG}; 6 | 7 | /// Used to clear the external interrupt pending register for the user button without moving the EXTI peripheral into global static state. 8 | /// 9 | /// # Note 10 | /// This does modify hardware register EXTI_PR1.PR0 and should probably only be called from `EXTI0` interrupt context 11 | /// 12 | /// # Example 13 | /// 14 | /// ``` 15 | /// #[interrupt] 16 | /// fn EXTI0() { 17 | /// // If we don't clear the interrupt to signal it's been serviced, it will continue to fire. 18 | /// button::interrupt::clear(); 19 | /// } 20 | /// ``` 21 | pub fn clear() { 22 | unsafe { 23 | let exti = &(*stm32f3xx_hal::pac::EXTI::ptr()); 24 | exti.pr1.write(|w| w.pr0().set_bit()) 25 | } 26 | } 27 | 28 | pub enum TriggerMode { 29 | Rising, 30 | Falling, 31 | Both, 32 | } 33 | 34 | /// Configures and enables interrupt for the `UserButton` on PA0. 35 | /// 36 | /// # Example 37 | /// 38 | /// ``` 39 | /// let device_periphs = pac::Peripherals::take().unwrap(); 40 | /// button::interrupt::enable(&device_periphs.EXTI, &device_periphs.SYSCFG, TriggerMode::Rising); 41 | /// ``` 42 | pub fn enable(external_interrupts: &EXTI, sysconfig: &SYSCFG, mode: TriggerMode) { 43 | // See chapter 14 of the reference manual 44 | // https://www.st.com/content/ccc/resource/technical/document/reference_manual/4a/19/6e/18/9d/92/43/32/DM00043574.pdf/files/DM00043574.pdf/jcr:content/translations/en.DM00043574.pdf 45 | 46 | configure_exti0(&external_interrupts.imr1); 47 | map_exti0_to_pa0(&sysconfig.exticr1); 48 | 49 | match mode { 50 | TriggerMode::Rising => configure_rising_edge_trigger(&external_interrupts.rtsr1), 51 | TriggerMode::Falling => configure_falling_edge_trigger(&external_interrupts.ftsr1), 52 | TriggerMode::Both => { 53 | configure_rising_edge_trigger(&external_interrupts.rtsr1); 54 | configure_falling_edge_trigger(&external_interrupts.ftsr1); 55 | } 56 | } 57 | 58 | enable_exti0(); 59 | } 60 | 61 | fn configure_exti0(interrupt_mask: &IMR1) { 62 | interrupt_mask.modify(|_, w| w.mr0().set_bit()) 63 | } 64 | 65 | fn map_exti0_to_pa0(external_interrupt_config: &EXTICR1) { 66 | const PORT_A_CONFIG: u8 = 0x000; 67 | external_interrupt_config.modify(|_, w| unsafe { w.exti0().bits(PORT_A_CONFIG) }); 68 | } 69 | 70 | fn configure_rising_edge_trigger(rising_trigger_select: &RTSR1) { 71 | rising_trigger_select.modify(|_, w| w.tr0().set_bit()) 72 | } 73 | 74 | fn configure_falling_edge_trigger(falling_trigger_select: &FTSR1) { 75 | falling_trigger_select.modify(|_, w| w.tr0().set_bit()) 76 | } 77 | 78 | fn enable_exti0() { 79 | unsafe { 80 | NVIC::unmask(Interrupt::EXTI0); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/button/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provides access to the user button on PA0 2 | pub mod interrupt; 3 | 4 | use stm32f3xx_hal::gpio::gpioa::PA0; 5 | use stm32f3xx_hal::gpio::{Input, gpioa}; 6 | use switch_hal::{ActiveHigh, InputSwitch, IntoSwitch, Switch}; 7 | 8 | /// Wrapper struct around `ActiveHighButton>` 9 | /// The user button has an external pull down resistor and low pass filter circuit. 10 | pub struct UserButton(Switch, ActiveHigh>); 11 | 12 | impl UserButton { 13 | /// Typesafe constructor for the user button peripheral on PA0. 14 | /// It's impossible to construct this button with the wrong pin or pin state. 15 | /// It's also impossible to construct more than one `UserButton` instance because `gpioa.pa0` is moved upon construction. 16 | pub fn new(pa0: PA0, moder: &mut gpioa::MODER, pupdr: &mut gpioa::PUPDR) -> Self { 17 | // This button is equipped with an external pull-down and so there is 18 | // no need to use the internal one. 19 | UserButton(pa0.into_floating_input(moder, pupdr).into_active_high_switch()) 20 | } 21 | } 22 | 23 | impl InputSwitch for UserButton { 24 | type Error = core::convert::Infallible; 25 | fn is_active(&self) -> Result { 26 | self.0.is_active() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/compass.rs: -------------------------------------------------------------------------------- 1 | use accelerometer::vector::{F32x3, I16x3}; 2 | use accelerometer::{Accelerometer, RawAccelerometer}; 3 | use stm32f3xx_hal::gpio; 4 | use stm32f3xx_hal::gpio::{gpiob, OpenDrain}; 5 | use stm32f3xx_hal::i2c; 6 | use stm32f3xx_hal::pac; 7 | use stm32f3xx_hal::prelude::*; 8 | use stm32f3xx_hal::rcc; 9 | 10 | 11 | type Lsm303 = 12 | lsm303dlhc::Lsm303dlhc>, gpiob::PB7>)>>; 13 | 14 | pub struct Compass { 15 | lsm303dlhc: Lsm303, 16 | } 17 | 18 | impl Compass { 19 | /// Initialize the onboard Lsm303dhlc e-Compass 20 | pub fn new( 21 | pb6: gpiob::PB6, 22 | pb7: gpiob::PB7, 23 | mode: &mut gpiob::MODER, 24 | otype: &mut gpiob::OTYPER, 25 | alternate_function_low: &mut gpiob::AFRL, 26 | i2c1: pac::I2C1, 27 | clocks: rcc::Clocks, 28 | advanced_periph_bus: &mut rcc::APB1, 29 | ) -> Result { 30 | /* 31 | * Pinout: 32 | * PB6 -> SCL (clock) 33 | * PB7 -> SDA (data) 34 | * PE2 -> DRDY (magnometer data ready) 35 | * PE4 -> INT1 (configurable interrupt 1) 36 | * PE5 -> INT2 (configurable interrupt 2) 37 | * lsm303hdlc driver uses continuos mode, so no need to wait for interrupts on DRDY 38 | */ 39 | let scl = pb6.into_af4_open_drain(mode, otype, alternate_function_low); 40 | let sda = pb7.into_af4_open_drain(mode, otype, alternate_function_low); 41 | let i2c = i2c::I2c::new(i2c1, (scl, sda), 400_000.Hz(), clocks, advanced_periph_bus); 42 | 43 | let lsm303dhlc = Lsm303::new(i2c)?; 44 | Ok(Compass { 45 | lsm303dlhc: lsm303dhlc, 46 | }) 47 | } 48 | 49 | /// Read the raw magnetometer data 50 | pub fn mag_raw(&mut self) -> Result { 51 | let reading = self.lsm303dlhc.mag()?; 52 | Ok(I16x3::new(reading.x, reading.y, reading.z)) 53 | } 54 | 55 | /// Consume the Compass and return the underlying Lsm303dhlc 56 | pub fn into_lsm303dlhc(self) -> Lsm303 { 57 | self.lsm303dlhc 58 | } 59 | } 60 | 61 | impl RawAccelerometer for Compass { 62 | type Error = i2c::Error; 63 | 64 | /// Read the raw accelerometer data 65 | fn accel_raw(&mut self) -> Result> { 66 | let reading = self.lsm303dlhc.accel()?; 67 | Ok(I16x3::new(reading.x, reading.y, reading.z)) 68 | } 69 | } 70 | 71 | /// Reads Accelerometer data in G-Force 72 | /// 73 | /// # Warning 74 | /// This is hard coded for the default settings because the driver doesn't provide a way 75 | /// to read the necessary registers to calculate it based on current settings. 76 | /// If you take control of the underlying device driver and change settings, 77 | /// this will not calculate the correct G-Force values. 78 | impl Accelerometer for Compass { 79 | type Error = i2c::Error; 80 | fn accel_norm(&mut self) -> Result> { 81 | let reading = self.accel_raw()?; 82 | /* 83 | * LA_FS (linear acceleration measurment range [full scale]) 84 | * can be +/-2, +/-4, +/-8, or +/- 16 85 | * LA_So (Linear acceleration sensitivity) can be 1,2,4, or 12 86 | * and is measured in milli-G / LSB 87 | * The driver provides a way to set the range/sensitivy, 88 | * but no way to read it, so we just hard code the default settings here (for now?). 89 | * 90 | * At +/-2g, we get 1mg/LSB (1 mg/bit) resolution. 91 | * The device returns a 16 bit result. 92 | * magnitude g / (1 mg / bit) = 93 | * +/- 2g range = 4g magnitude 94 | * 4g / 65535 bits = 4g/(1<<16) = 0.000061 g / bit 95 | * 2g / 32768 bits = 2g/(1<<15) = 0.000061 g / bit 96 | * 97 | * I _think_ the general equation is: 98 | * scale_factor = magnitude / #of_bits * sensitivity 99 | * scale_factor = (abs(range)*2) / #of_bits * sensitivity 100 | * scale_factor = abs(range) / (#of_bits/2) * sensitivity 101 | * sf(+/-2g) = 4g / 65535 bits * 1mg/LSB = 0.000061 g 102 | * sf(+/-4g) = 8g / 65535 bits * 2mg/LSB = 0.000244 g 103 | * sf(+/-8g) = 16g / 65535 bits * 4mg/LSB = 0.000976 g 104 | * sf(+/-16g) = 32g / 65535 bits * 12mg/LSB = 0.005859 g 105 | * 106 | * NOTE: This also does not account for temperature variance. 107 | */ 108 | const MAGNITUDE: i32 = 4; 109 | const NO_OF_BITS: i32 = 1 << 16; 110 | const SENSITIVITY: i32 = 1; 111 | const SCALE_FACTOR: f32 = (MAGNITUDE as f32 / NO_OF_BITS as f32) * SENSITIVITY as f32; 112 | Ok(F32x3::new ( 113 | reading.x as f32 * SCALE_FACTOR, 114 | reading.y as f32 * SCALE_FACTOR, 115 | reading.z as f32 * SCALE_FACTOR, 116 | )) 117 | } 118 | 119 | fn sample_rate(&mut self) -> Result::Error>> { 120 | // we don't expose a way to change this, so hard coded to 400Hz right now 121 | // it should really be read from the device in case someone snags the raw lsm303dlhc struct, 122 | // but the driver does't give us a way to read it back from the device 123 | Ok(400.0) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/leds.rs: -------------------------------------------------------------------------------- 1 | //! Provides access to User LEDs LD3-LD10 2 | use stm32f3xx_hal::gpio::gpioe; 3 | use stm32f3xx_hal::gpio::{Output, PushPull}; 4 | 5 | use switch_hal::{ActiveHigh, IntoSwitch, OutputSwitch, Switch}; 6 | 7 | use core::slice::Iter; 8 | use core::iter::FusedIterator; 9 | 10 | /// LED compass direction as noted on the board 11 | #[derive(Clone, Copy, Eq, PartialEq)] 12 | pub enum Direction 13 | { 14 | North, 15 | NorthEast, 16 | East, 17 | SouthEast, 18 | South, 19 | SouthWest, 20 | West, 21 | NorthWest, 22 | } 23 | 24 | impl Direction { 25 | /// Provides an iterator starting with North 26 | /// and moving clockwise around the compass 27 | /// e.g. N -> NE -> E, etc. 28 | pub fn iter() -> Iter<'static, Direction> { 29 | static DIRECTIONS: [Direction; 8] = [ 30 | Direction::North, 31 | Direction::NorthEast, 32 | Direction::East, 33 | Direction::SouthEast, 34 | Direction::South, 35 | Direction::SouthWest, 36 | Direction::West, 37 | Direction::NorthWest 38 | ]; 39 | DIRECTIONS.iter() 40 | } 41 | } 42 | 43 | type Led = Switch>, ActiveHigh>; 44 | 45 | pub struct Leds { 46 | /// North 47 | pub ld3: Led, 48 | /// NorthWest 49 | pub ld4: Led, 50 | /// NorthEast 51 | pub ld5: Led, 52 | /// West 53 | pub ld6: Led, 54 | /// East 55 | pub ld7: Led, 56 | /// SouthWest 57 | pub ld8: Led, 58 | /// SouthEast 59 | pub ld9: Led, 60 | /// South 61 | pub ld10: Led, 62 | } 63 | 64 | impl Leds { 65 | /// Initializes the user LEDs to OFF 66 | pub fn new( 67 | pe8: gpioe::PE8, 68 | pe9: gpioe::PE9, 69 | pe10: gpioe::PE10, 70 | pe11: gpioe::PE11, 71 | pe12: gpioe::PE12, 72 | pe13: gpioe::PE13, 73 | pe14: gpioe::PE14, 74 | pe15: gpioe::PE15, 75 | moder: &mut gpioe::MODER, 76 | otyper: &mut gpioe::OTYPER, 77 | ) -> Self { 78 | let mut leds = Leds { 79 | ld3: pe9 80 | .into_push_pull_output(moder, otyper) 81 | .downgrade() 82 | .into_active_high_switch(), 83 | ld4: pe8 84 | .into_push_pull_output(moder, otyper) 85 | .downgrade() 86 | .into_active_high_switch(), 87 | ld5: pe10 88 | .into_push_pull_output(moder, otyper) 89 | .downgrade() 90 | .into_active_high_switch(), 91 | ld6: pe15 92 | .into_push_pull_output(moder, otyper) 93 | .downgrade() 94 | .into_active_high_switch(), 95 | ld7: pe11 96 | .into_push_pull_output(moder, otyper) 97 | .downgrade() 98 | .into_active_high_switch(), 99 | ld8: pe14 100 | .into_push_pull_output(moder, otyper) 101 | .downgrade() 102 | .into_active_high_switch(), 103 | ld9: pe12 104 | .into_push_pull_output(moder, otyper) 105 | .downgrade() 106 | .into_active_high_switch(), 107 | ld10: pe13 108 | .into_push_pull_output(moder, otyper) 109 | .downgrade() 110 | .into_active_high_switch(), 111 | }; 112 | 113 | for led in &mut leds { 114 | led.off().ok(); 115 | } 116 | 117 | leds 118 | } 119 | 120 | /// Mutably borrow a LED by the given direction (as noted on the board) 121 | /// 122 | /// # Example 123 | /// 124 | /// ``` 125 | /// let southLed = leds.for_direction(Direction::South); 126 | /// southLed.on().ok(); 127 | /// ``` 128 | pub fn for_direction(&mut self, direction: Direction) -> &mut Led { 129 | match direction { 130 | Direction::North => &mut self.ld3, 131 | Direction::NorthEast => &mut self.ld5, 132 | Direction::East => &mut self.ld7, 133 | Direction::SouthEast => &mut self.ld9, 134 | Direction::South => &mut self.ld10, 135 | Direction::SouthWest => &mut self.ld8, 136 | Direction::West => &mut self.ld6, 137 | Direction::NorthWest => &mut self.ld4, 138 | } 139 | } 140 | 141 | /// Provides a mutable iterator for iterating over the on board leds. 142 | /// Starts at ld3 (N) and moves clockwise. 143 | /// Stops once it has iterated through all 8 leds. 144 | /// 145 | /// # Examples 146 | /// 147 | /// Iterate over the leds clockwise 148 | /// 149 | /// ``` 150 | /// let ms_delay = 50u16; 151 | /// for led in &mut leds { 152 | /// led.on().ok(); 153 | /// delay.delay_ms(ms_delay); 154 | /// led.off().ok(); 155 | /// delay.delay_ms(ms_delay); 156 | /// } 157 | /// ``` 158 | /// 159 | /// Iterate over the leds counter clockwise 160 | /// 161 | /// ``` 162 | /// let ms_delay = 50u16; 163 | /// for led in leds.iter_mut().rev() { 164 | /// led.on().ok(); 165 | /// delay.delay_ms(ms_delay); 166 | /// led.off().ok(); 167 | /// delay.delay_ms(ms_delay); 168 | /// } 169 | /// ``` 170 | pub fn iter_mut(&mut self) -> LedsMutIterator { 171 | LedsMutIterator::new(self) 172 | } 173 | 174 | /// Consumes the `Leds` struct and returns an array, 175 | /// where index 0 is N and each incrementing index. 176 | /// Rotates clockwise around the compass. 177 | /// 178 | /// # Warning 179 | /// 180 | /// This function is maintained solely for some level of compatibility with the old F3 crate. 181 | /// 182 | /// [`Self::iter_mut()`] should be prefered. 183 | /// Testing suggests that using [`Self::iter_mut()`] results in an ~800 byte 184 | /// reduction in final binary size. 185 | pub fn into_array(self) -> [Led; 8] { 186 | [ 187 | self.ld3, //N 188 | self.ld5, //NE 189 | self.ld7, //E 190 | self.ld9, //SE 191 | self.ld10, //S 192 | self.ld8, //SW 193 | self.ld6, //W 194 | self.ld4, //NW 195 | ] 196 | } 197 | } 198 | 199 | impl<'a> IntoIterator for &'a mut Leds { 200 | type Item = &'a mut Led; 201 | type IntoIter = LedsMutIterator<'a>; 202 | 203 | fn into_iter(self) -> Self::IntoIter { 204 | self.iter_mut() 205 | } 206 | } 207 | 208 | const ITERATOR_SIZE: usize = 8; 209 | 210 | pub struct LedsMutIterator<'a> { 211 | index: usize, 212 | index_back: usize, 213 | leds: &'a mut Leds 214 | } 215 | 216 | impl<'a> LedsMutIterator<'a> { 217 | fn new(leds: &'a mut Leds) -> Self { 218 | LedsMutIterator { index: 0, index_back: ITERATOR_SIZE, leds } 219 | } 220 | 221 | fn len(&self) -> usize { 222 | self.index_back - self.index 223 | } 224 | 225 | fn size_hint(&self) -> (usize, Option) { 226 | let length = self.len(); 227 | (length, Some(length)) 228 | } 229 | } 230 | 231 | impl<'a> Iterator for LedsMutIterator<'a> { 232 | type Item = &'a mut Led; 233 | fn next(&mut self) -> Option { 234 | if self.len() == 0 { 235 | None 236 | } else { 237 | let current = unsafe { 238 | //Safety: Each branch is only executed once, 239 | // and only if there are elements left to be returned, 240 | // so we can not possibly alias a mutable reference. 241 | // This depends on DoubleEndedIterator and ExactSizedIterator being implemented correctly. 242 | // If len() does not return the correct number of remaining elements, 243 | // this becomes unsound. 244 | match self.index { 245 | 0 => Some(&mut *(&mut self.leds.ld3 as *mut _)), //N 246 | 1 => Some(&mut *(&mut self.leds.ld5 as *mut _)), //NE 247 | 2 => Some(&mut *(&mut self.leds.ld7 as *mut _)), //E 248 | 3 => Some(&mut *(&mut self.leds.ld9 as *mut _)), //SE 249 | 4 => Some(&mut *(&mut self.leds.ld10 as *mut _)), //S 250 | 5 => Some(&mut *(&mut self.leds.ld8 as *mut _)), //SW 251 | 6 => Some(&mut *(&mut self.leds.ld6 as *mut _)), //W 252 | 7 => Some(&mut *(&mut self.leds.ld4 as *mut _)), //NW 253 | _ => None 254 | } 255 | }; 256 | self.index += 1; 257 | current 258 | } 259 | } 260 | 261 | // Because we implement ExactSizedIterator, we need to ensure size_hint returns the right length 262 | fn size_hint(&self) -> (usize, Option) { 263 | self.size_hint() 264 | } 265 | } 266 | 267 | impl<'a> DoubleEndedIterator for LedsMutIterator<'a> { 268 | fn next_back(&mut self) -> Option { 269 | if self.len() == 0 { 270 | None 271 | } else { 272 | let current = unsafe { 273 | //Safety: Each branch is only executed once, 274 | // and only if there are elements left to be returned, 275 | // so we can not possibly alias a mutable reference. 276 | // This depends on Iterator and ExactSizedIterator being implemented correctly. 277 | // If len() does not return the correct number of remaining elements, 278 | // this becomes unsound. 279 | match self.index_back { 280 | // Because we're going backwards and index_back is a usize, 281 | // We use a one based index so we don't go negative 282 | 0 => None, //done 283 | 1 => Some(&mut *(&mut self.leds.ld3 as *mut _)), //N 284 | 2 => Some(&mut *(&mut self.leds.ld5 as *mut _)), //NE 285 | 3 => Some(&mut *(&mut self.leds.ld7 as *mut _)), //E 286 | 4 => Some(&mut *(&mut self.leds.ld9 as *mut _)), //SE 287 | 5 => Some(&mut *(&mut self.leds.ld10 as *mut _)), //S 288 | 6 => Some(&mut *(&mut self.leds.ld8 as *mut _)), //SW 289 | 7 => Some(&mut *(&mut self.leds.ld6 as *mut _)), //W 290 | 8 => Some(&mut *(&mut self.leds.ld4 as *mut _)), //NW 291 | _ => None //can't happen 292 | } 293 | }; 294 | self.index_back -= 1; 295 | current 296 | } 297 | } 298 | } 299 | 300 | impl<'a> ExactSizeIterator for LedsMutIterator<'a> { 301 | fn len(&self) -> usize { 302 | self.len() 303 | } 304 | } 305 | 306 | ///Marker trait that indicates LedsMutIterator never starts returning Some after returning None 307 | impl<'a> FusedIterator for LedsMutIterator<'a> {} -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub use accelerometer; 4 | pub use lsm303dlhc; 5 | pub use stm32f3xx_hal; 6 | pub use switch_hal; 7 | 8 | pub mod button; 9 | pub mod compass; 10 | pub mod leds; 11 | 12 | /// Signals the process to go into low power mode until an interrupt occurs 13 | pub fn wait_for_interrupt() { 14 | cortex_m::asm::wfi() 15 | } 16 | --------------------------------------------------------------------------------