├── .cargo └── config ├── .github └── workflows │ ├── format.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples ├── msp432-launchpad-for-ethernet │ ├── .cargo │ │ └── config │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── openocd.cfg │ ├── openocd.gdb │ └── src │ │ └── main.rs ├── tiva-c-connected-launchpad │ ├── .cargo │ │ └── config │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── openocd.cfg │ ├── openocd.gdb │ └── src │ │ └── main.rs └── tiva-c-launchpad │ ├── .cargo │ └── config │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── openocd.cfg │ ├── openocd.gdb │ └── src │ └── main.rs ├── tm4c-hal ├── Cargo.toml ├── README.md └── src │ ├── bb.rs │ ├── delay.rs │ ├── eeprom.rs │ ├── gpio.rs │ ├── i2c.rs │ ├── lib.rs │ ├── serial.rs │ ├── sysctl.rs │ └── time.rs ├── tm4c123x-hal ├── .cargo │ └── config ├── Cargo.toml ├── README.md ├── build.rs ├── memory.x └── src │ ├── eeprom.rs │ ├── gpio.rs │ ├── hib.rs │ ├── i2c.rs │ ├── lib.rs │ ├── prelude.rs │ ├── pwm.rs │ ├── serial.rs │ ├── spi.rs │ ├── sysctl.rs │ ├── time.rs │ └── timer.rs └── tm4c129x-hal ├── .cargo └── config ├── Cargo.toml ├── README.md ├── build.rs ├── memory.x └── src ├── gpio.rs ├── hib.rs ├── i2c.rs ├── lib.rs ├── prelude.rs ├── serial.rs ├── spi.rs ├── sysctl.rs └── time.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 3 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | check: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Add Tool 13 | run: rustup component add rustfmt 14 | - name: Check Format 15 | run: cargo fmt -- --check 16 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Add Target 13 | run: rustup target add thumbv7em-none-eabi 14 | - name: Build 15 | run: cargo build --verbose --target=thumbv7em-none-eabi 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | **/target/ 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "tm4c123x-hal", 4 | "tm4c129x-hal", 5 | "tm4c-hal", 6 | "examples/tiva-c-launchpad", 7 | "examples/tiva-c-connected-launchpad", 8 | "examples/msp432-launchpad-for-ethernet", 9 | ] 10 | 11 | [profile.release] 12 | codegen-units = 1 # better optimizations 13 | debug = true # symbols are nice and they don't increase the size on Flash 14 | lto = true # better optimizations 15 | -------------------------------------------------------------------------------- /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) 2017-2018 Jonathan 'theJPster' Pallant 2 | Copyright (c) 2017-2018 Jorge Aparicio 3 | Copyright (c) 2018 David Wood 4 | Copyright (c) 2018 Marc Poulhiès 5 | 6 | Permission is hereby granted, free of charge, to any 7 | person obtaining a copy of this software and associated 8 | documentation files (the "Software"), to deal in the 9 | Software without restriction, including without 10 | limitation the rights to use, copy, modify, merge, 11 | publish, distribute, sublicense, and/or sell copies of 12 | the Software, and to permit persons to whom the Software 13 | is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice 17 | shall be included in all copies or substantial portions 18 | of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 21 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 22 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 23 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 24 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 27 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | DEALINGS IN THE SOFTWARE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `tm4c129x-hal` and `tm4c123x-hal` 2 | 3 | > An [Embedded HAL] (Hardware Abstraction Layer) for the [TM4C129x] and [TM4C123x] families of microcontrollers from Texas Instruments. 4 | 5 | [Embedded HAL]: https://crates.io/crates/embedded-hal 6 | [TM4C123x]: https://www.ti.com/product/TM4C123GH6PM 7 | [TM4C129x]: https://www.ti.com/product/TM4C1294NCPDT 8 | 9 | These microcontrollers are based on the Arm Cortex-M4F processor core and derived from the earlier TI/Luminary Micro LM4 and LM3 series MCUs. This HAL may work on an LM4F series MCU, but there are no guarantees. A full list of TM4C series microcontrollers is available from TI in [Document SPMT285D](https://www.ti.com/lit/sg/spmt285d/spmt285d.pdf). 10 | 11 | ## Crates 12 | 13 | This repo comprises: 14 | 15 | * `tm4c123x-hal` [![tm4c123x-hal version](https://img.shields.io/crates/v/tm4c123x-hal.svg)](https://crates.io/crates/tm4c123x-hal/) - a HAL for the TM4C123GH6PM and related microcontrollers 16 | * `tm4c129x-hal` [![tm4c129x-hal version](https://img.shields.io/crates/v/tm4c129x-hal.svg)](https://crates.io/crates/tm4c129x-hal/) - a HAL for the TM4C1294NCPDT and related microcontrollers 17 | * `tm4c-hal` [![tm4c-hal version](https://img.shields.io/crates/v/tm4c-hal.svg)](https://crates.io/crates/tm4c-hal/) - drivers and HAL implementation that is common to both the above MCU families 18 | 19 | ## Example Hardware 20 | 21 | These crates are tested on the following Tiva-C Launchpad boards: 22 | 23 | * Tiva-C Series TM4C123G Launchpad, [EK-TM4C123GXL](https://www.ti.com/tool/EK-TM4C123GXL) 24 | * Tiva-C Series TM4C1294 Connected Launchpad, [EK-TM4C1294XL](https://www.ti.com/tool/EK-TM4C1294XL) 25 | * Tiva-C Series TM4C129E Crypto Launchpad, [EK-TM4C129EXL](http://www.ti.com/tool/EK-TM4C129EXL) 26 | 27 | ## Example projects 28 | 29 | The authors are aware of the following projects which use one or other (or both) of these crates: 30 | 31 | * [Monotron](https://github.com/thejpster/monotron), a 1980s-style retro computer with VGA output 32 | 33 | ## Documentation 34 | 35 | See https://docs.rs/tm4c129x-hal and https://docs.rs/tm4c123x-hal. 36 | 37 | ## License 38 | 39 | Licensed under either of 40 | 41 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 42 | http://www.apache.org/licenses/LICENSE-2.0) 43 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 44 | 45 | at your option. 46 | 47 | ## Contribution 48 | 49 | Unless you explicitly state otherwise, any contribution intentionally submitted 50 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 51 | dual licensed as above, without any additional terms or conditions. 52 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/.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-rs run --chip MSP432E411Y --protocol swd --speed 4000" 8 | 9 | rustflags = [ 10 | "-C", "link-arg=-Tlink.x", 11 | ] 12 | 13 | [build] 14 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 15 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ 6 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "9names", 4 | ] 5 | edition = "2018" 6 | readme = "README.md" 7 | name = "msp432-launchpad-for-ethernet" 8 | version = "0.1.0" 9 | license = "MIT OR Apache-2.0" 10 | 11 | [dependencies] 12 | cortex-m = "0.7" 13 | cortex-m-rt = "0.7" 14 | cortex-m-semihosting = "0.5" 15 | panic-halt = "0.2.0" 16 | 17 | # MSP432E401Y is functionally equivalent to TM4C1294NCPDT as used in Tiva C Connected Launchpad. 18 | # The only difference is MSP432 has SimpleLink functions in ROM 19 | # and TM4C has TivaWare functions in ROM. 20 | # As long as tm4c129x-hal never uses ROM functions these chips are, as far as I know, 100% compatible. 21 | # They certainly have the same memory map, appear to have the same periperals and share the same errata list. 22 | [dependencies.tm4c129x-hal] 23 | # version = "0.7.0" 24 | path = "../../tm4c129x-hal" 25 | features = ["rt"] 26 | 27 | # this lets you use `cargo fix`! 28 | [[bin]] 29 | name = "msp432-launchpad-for-ethernet" 30 | test = false 31 | bench = false 32 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/README.md: -------------------------------------------------------------------------------- 1 | # MSP432 Launchpad for Ethernet Demo Application 2 | 3 | > For the MSP432 Launchpad developement kit for Ethernet SimpleLink MCU, [MSP-EXP432E401Y](https://www.ti.com/tool/MSP-EXP432E401Y) 4 | 5 | ## License 6 | 7 | This example is licensed under either of 8 | 9 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 10 | http://www.apache.org/licenses/LICENSE-2.0) 11 | 12 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 13 | 14 | at your option. 15 | 16 | ## Contribution 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally submitted 19 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 20 | dual licensed as above, without any additional terms or conditions. 21 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the MSP432 Launchpad for Ethernet board 2 | 3 | source [find board/ti_msp432_launchpad.cfg] 4 | 5 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | break DefaultHandler 8 | break UserHardFault 9 | break rust_begin_unwind 10 | 11 | # *try* to stop at the user entry point (it might be gone due to inlining) 12 | break main 13 | 14 | monitor arm semihosting enable 15 | 16 | # # send captured ITM to the file itm.fifo 17 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 18 | # # 8000000 must match the core clock frequency 19 | # monitor tpiu config internal itm.txt uart off 8000000 20 | 21 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 22 | # # 8000000 must match the core clock frequency 23 | # # 2000000 is the frequency of the SWO pin 24 | # monitor tpiu config external uart off 8000000 2000000 25 | 26 | # # enable ITM port 0 27 | # monitor itm port 0 on 28 | 29 | load 30 | 31 | # start the process but immediately halt the processor 32 | stepi 33 | -------------------------------------------------------------------------------- /examples/msp432-launchpad-for-ethernet/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics 5 | 6 | use core::fmt::Write; 7 | use cortex_m_rt::entry; 8 | use tm4c129x_hal::{self as hal, prelude::*}; 9 | 10 | #[entry] 11 | fn main() -> ! { 12 | let cp = hal::CorePeripherals::take().unwrap(); 13 | let p = hal::Peripherals::take().unwrap(); 14 | 15 | let mut sc = p.SYSCTL.constrain(); 16 | sc.clock_setup.oscillator = hal::sysctl::Oscillator::Main( 17 | hal::sysctl::CrystalFrequency::_25mhz, 18 | hal::sysctl::SystemClock::UsePll(hal::sysctl::PllOutputFrequency::_120mhz), 19 | ); 20 | let clocks = sc.clock_setup.freeze(); 21 | 22 | let mut porta = p.GPIO_PORTA_AHB.split(&sc.power_control); 23 | let portn = p.GPIO_PORTN.split(&sc.power_control); 24 | let portf = p.GPIO_PORTF_AHB.split(&sc.power_control); 25 | 26 | // Activate UART 27 | let mut uart = hal::serial::Serial::uart0( 28 | p.UART0, 29 | porta 30 | .pa1 31 | .into_af_push_pull::(&mut porta.control), 32 | porta 33 | .pa0 34 | .into_af_push_pull::(&mut porta.control), 35 | (), 36 | (), 37 | 115200_u32.bps(), 38 | hal::serial::NewlineMode::SwapLFtoCRLF, 39 | &clocks, 40 | &sc.power_control, 41 | ); 42 | let mut led1 = portn.pn1.into_push_pull_output(); 43 | let mut led2 = portn.pn0.into_push_pull_output(); 44 | let mut led3 = portf.pf4.into_push_pull_output(); 45 | let mut led4 = portf.pf0.into_push_pull_output(); 46 | 47 | let mut counter = 0u32; 48 | let mut delay = cortex_m::delay::Delay::new(cp.SYST, 120_000_000u32); 49 | loop { 50 | writeln!(uart, "Hello, world! counter={}", counter).unwrap(); 51 | let led_state = counter % 4; 52 | if led_state == 0 { 53 | led1.set_high(); 54 | led2.set_low(); 55 | led3.set_low(); 56 | led4.set_low(); 57 | } 58 | if led_state == 1 { 59 | led1.set_low(); 60 | led2.set_high(); 61 | led3.set_low(); 62 | led4.set_low(); 63 | } 64 | if led_state == 2 { 65 | led1.set_low(); 66 | led2.set_low(); 67 | led3.set_high(); 68 | led4.set_low(); 69 | } 70 | if led_state == 3 { 71 | led1.set_low(); 72 | led2.set_low(); 73 | led3.set_low(); 74 | led4.set_high(); 75 | } 76 | 77 | counter = counter.wrapping_add(1); 78 | delay.delay_ms(200); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/.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 | 8 | rustflags = [ 9 | "-C", "link-arg=-Tlink.x", 10 | ] 11 | 12 | [build] 13 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 14 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ 6 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Jonathan 'theJPster' Pallant ", 4 | ] 5 | edition = "2018" 6 | readme = "README.md" 7 | name = "tiva-c-connected-launchpad" 8 | version = "0.1.0" 9 | 10 | [dependencies] 11 | cortex-m = "0.7" 12 | cortex-m-rt = "0.7" 13 | cortex-m-semihosting = "0.5" 14 | panic-halt = "0.2.0" 15 | 16 | [dependencies.tm4c129x-hal] 17 | # version = "0.7.0" 18 | path = "../../tm4c129x-hal" 19 | features = ["rt"] 20 | 21 | # this lets you use `cargo fix`! 22 | [[bin]] 23 | name = "tiva-c-connected-launchpad" 24 | test = false 25 | bench = false 26 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/README.md: -------------------------------------------------------------------------------- 1 | # Tiva-C Connected Launchpad Demo Application 2 | 3 | > For the Tiva-C Series TM4C1294 Connected Launchpad, [EK-TM4C1294XL](https://www.ti.com/tool/EK-TM4C1294XL) 4 | 5 | ## License 6 | 7 | This template is licensed under either of 8 | 9 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 10 | http://www.apache.org/licenses/LICENSE-2.0) 11 | 12 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 13 | 14 | at your option. 15 | 16 | ## Contribution 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally submitted 19 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 20 | dual licensed as above, without any additional terms or conditions. 21 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the Tiva-C Connected Launchpad development board 2 | 3 | source [find board/ek-tm4c1294xl.cfg] 4 | 5 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | break DefaultHandler 8 | break UserHardFault 9 | break rust_begin_unwind 10 | 11 | # *try* to stop at the user entry point (it might be gone due to inlining) 12 | break main 13 | 14 | monitor arm semihosting enable 15 | 16 | # # send captured ITM to the file itm.fifo 17 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 18 | # # 8000000 must match the core clock frequency 19 | # monitor tpiu config internal itm.txt uart off 8000000 20 | 21 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 22 | # # 8000000 must match the core clock frequency 23 | # # 2000000 is the frequency of the SWO pin 24 | # monitor tpiu config external uart off 8000000 2000000 25 | 26 | # # enable ITM port 0 27 | # monitor itm port 0 on 28 | 29 | load 30 | 31 | # start the process but immediately halt the processor 32 | stepi 33 | -------------------------------------------------------------------------------- /examples/tiva-c-connected-launchpad/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics 5 | 6 | use core::fmt::Write; 7 | use cortex_m_rt::entry; 8 | use tm4c129x_hal::{self as hal, prelude::*}; 9 | 10 | #[entry] 11 | fn main() -> ! { 12 | let p = hal::Peripherals::take().unwrap(); 13 | 14 | let mut sc = p.SYSCTL.constrain(); 15 | sc.clock_setup.oscillator = hal::sysctl::Oscillator::Main( 16 | hal::sysctl::CrystalFrequency::_16mhz, 17 | hal::sysctl::SystemClock::UsePll(hal::sysctl::PllOutputFrequency::_120mhz), 18 | ); 19 | let clocks = sc.clock_setup.freeze(); 20 | 21 | let mut porta = p.GPIO_PORTA_AHB.split(&sc.power_control); 22 | 23 | // Activate UART 24 | let mut uart = hal::serial::Serial::uart0( 25 | p.UART0, 26 | porta 27 | .pa1 28 | .into_af_push_pull::(&mut porta.control), 29 | porta 30 | .pa0 31 | .into_af_push_pull::(&mut porta.control), 32 | (), 33 | (), 34 | 115200_u32.bps(), 35 | hal::serial::NewlineMode::SwapLFtoCRLF, 36 | &clocks, 37 | &sc.power_control, 38 | ); 39 | 40 | let mut counter = 0u32; 41 | loop { 42 | writeln!(uart, "Hello, world! counter={}", counter).unwrap(); 43 | counter = counter.wrapping_add(1); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/.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 | 8 | rustflags = [ 9 | "-C", "link-arg=-Tlink.x", 10 | ] 11 | 12 | [build] 13 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 14 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | .gdb_history 4 | Cargo.lock 5 | target/ 6 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Jonathan 'theJPster' Pallant ", 4 | ] 5 | edition = "2018" 6 | readme = "README.md" 7 | name = "tiva-c-launchpad" 8 | version = "0.1.0" 9 | 10 | [dependencies] 11 | cortex-m = "0.7" 12 | cortex-m-rt = "0.7" 13 | cortex-m-semihosting = "0.5" 14 | panic-halt = "0.2.0" 15 | 16 | [dependencies.tm4c123x-hal] 17 | # version = "0.8.0" 18 | path = "../../tm4c123x-hal" 19 | features = ["rt"] 20 | 21 | # this lets you use `cargo fix`! 22 | [[bin]] 23 | name = "tiva-c-launchpad" 24 | test = false 25 | bench = false 26 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/README.md: -------------------------------------------------------------------------------- 1 | # Tiva-C Launchpad Demo Application 2 | 3 | > For the Tiva-C Series TM4C123G Launchpad, [EK-TM4C123GXL](https://www.ti.com/tool/EK-TM4C123GXL) 4 | 5 | ## License 6 | 7 | This template is licensed under either of 8 | 9 | - Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or 10 | http://www.apache.org/licenses/LICENSE-2.0) 11 | 12 | - MIT license ([LICENSE-MIT](../../LICENSE-MIT) or http://opensource.org/licenses/MIT) 13 | 14 | at your option. 15 | 16 | ## Contribution 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally submitted 19 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 20 | dual licensed as above, without any additional terms or conditions. 21 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/openocd.cfg: -------------------------------------------------------------------------------- 1 | # Sample OpenOCD configuration for the Tiva-C Launchpad development board 2 | 3 | source [find board/ti_ek-tm4c123gxl.cfg] 4 | 5 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # print demangled symbols 4 | set print asm-demangle on 5 | 6 | # detect unhandled exceptions, hard faults and panics 7 | break DefaultHandler 8 | break UserHardFault 9 | break rust_begin_unwind 10 | 11 | # *try* to stop at the user entry point (it might be gone due to inlining) 12 | break main 13 | 14 | monitor arm semihosting enable 15 | 16 | # # send captured ITM to the file itm.fifo 17 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 18 | # # 8000000 must match the core clock frequency 19 | # monitor tpiu config internal itm.txt uart off 8000000 20 | 21 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 22 | # # 8000000 must match the core clock frequency 23 | # # 2000000 is the frequency of the SWO pin 24 | # monitor tpiu config external uart off 8000000 2000000 25 | 26 | # # enable ITM port 0 27 | # monitor itm port 0 on 28 | 29 | load 30 | 31 | # start the process but immediately halt the processor 32 | stepi 33 | -------------------------------------------------------------------------------- /examples/tiva-c-launchpad/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics 5 | 6 | use core::fmt::Write; 7 | use cortex_m_rt::entry; 8 | use tm4c123x_hal::eeprom::{ 9 | Blocks, Eeprom, EepromAddress, EepromError, Erase, Read, Write as EepromWrite, 10 | }; 11 | use tm4c123x_hal::{self as hal, prelude::*}; 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let p = hal::Peripherals::take().unwrap(); 16 | 17 | let mut sc = p.SYSCTL.constrain(); 18 | sc.clock_setup.oscillator = hal::sysctl::Oscillator::Main( 19 | hal::sysctl::CrystalFrequency::_16mhz, 20 | hal::sysctl::SystemClock::UsePll(hal::sysctl::PllOutputFrequency::_80_00mhz), 21 | ); 22 | let clocks = sc.clock_setup.freeze(); 23 | 24 | let mut porta = p.GPIO_PORTA.split(&sc.power_control); 25 | 26 | let mut eeprom = Eeprom::new(p.EEPROM, &sc.power_control); 27 | 28 | match eeprom_test_all(&mut eeprom) { 29 | Ok(_) => { 30 | // Huzzah! 31 | } 32 | Err(code) => { 33 | panic!("Error detected while testing EEPROM: {}", code); 34 | } 35 | } 36 | 37 | // Activate UART 38 | let mut uart = hal::serial::Serial::uart0( 39 | p.UART0, 40 | porta 41 | .pa1 42 | .into_af_push_pull::(&mut porta.control), 43 | porta 44 | .pa0 45 | .into_af_push_pull::(&mut porta.control), 46 | (), 47 | (), 48 | 115200_u32.bps(), 49 | hal::serial::NewlineMode::SwapLFtoCRLF, 50 | &clocks, 51 | &sc.power_control, 52 | ); 53 | 54 | let mut counter = 0u32; 55 | loop { 56 | writeln!(uart, "Hello, world! counter={}", counter).unwrap(); 57 | counter = counter.wrapping_add(1); 58 | } 59 | } 60 | 61 | pub fn eeprom_test_write_read( 62 | eeprom: &mut Eeprom, 63 | address: &EepromAddress, 64 | data_to_write: &[u8], 65 | read_buffer: &mut [u8], 66 | ) -> Result<(), EepromError> { 67 | eeprom.write(address, &data_to_write)?; 68 | eeprom.read(address, data_to_write.len(), read_buffer)?; 69 | 70 | for (i, byte) in data_to_write.iter().enumerate() { 71 | assert_eq!(*byte, read_buffer[i], "Read data differs from written data"); 72 | } 73 | 74 | Ok(()) 75 | } 76 | 77 | pub fn eeprom_test_all(eeprom: &mut Eeprom) -> Result<(), EepromError> { 78 | let mut buffer = [0 as u8; 64]; // 64 byte read buffer 79 | 80 | // Sanity check for simple mapping from word offset to an EepromAddress 81 | let mut address = eeprom.word_index_to_address(52).unwrap(); 82 | assert_eq!(address.block(), 3, "Word 52 should be in block 3, offset 4"); 83 | assert_eq!( 84 | address.offset(), 85 | 4, 86 | "Word 52 should be in block 3, offset 4" 87 | ); 88 | 89 | // Sanity check for EepromAddress to word offset 90 | let word_index = eeprom.address_to_word_index(&address).unwrap(); 91 | assert_eq!( 92 | word_index, 52, 93 | "Word index for block 3, offset 4 should be 52" 94 | ); 95 | 96 | // Simplest case, middle of a block, no straddle 97 | let test_array_1: [u8; 4] = [1, 2, 3, 4]; 98 | eeprom_test_write_read(eeprom, &mut address, &test_array_1, &mut buffer)?; 99 | 100 | // Test boundry conditions for access that straddles a block 101 | let test_array_2: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 102 | let address_straddle_block = EepromAddress::new(0, 15); 103 | eeprom_test_write_read(eeprom, &address_straddle_block, &test_array_2, &mut buffer)?; 104 | 105 | eeprom.erase(&address_straddle_block, test_array_2.len())?; 106 | eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?; 107 | for i in 0..test_array_2.len() { 108 | assert_eq!(buffer[i], 0, "Buffer should be all 0's") 109 | } 110 | 111 | // Test the block erase using the straddle address and data 112 | eeprom.write(&address_straddle_block, &test_array_2)?; 113 | eeprom.erase_block(0)?; 114 | eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?; 115 | for i in 0..test_array_2.len() { 116 | match i { 117 | 0..=3 => { 118 | assert_eq!(buffer[i], 0, "Buffer[0..3] should be all 0's"); 119 | } 120 | _ => { 121 | assert_eq!( 122 | buffer[i], test_array_2[i], 123 | "Buffer[4..9] should match test_array_2" 124 | ) 125 | } 126 | } 127 | } 128 | 129 | Ok(()) 130 | } 131 | -------------------------------------------------------------------------------- /tm4c-hal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tm4c-hal" 3 | version = "0.4.2" 4 | authors = [ 5 | "Jorge Aparicio ", 6 | "Jonathan 'theJPster' Pallant ", 7 | "Marc Poulhiès ", 8 | "David Wood ", 9 | ] 10 | description = "Common bits of HAL for the TM4C123x/TM4C129x family of microcontrollers" 11 | keywords = ["arm", "cortex-m", "tm4c", "lm4f120", "hal"] 12 | categories = ["embedded", "hardware-support", "no-std"] 13 | license = "MIT OR Apache-2.0" 14 | edition = "2018" 15 | repository = "https://github.com/rust-embedded-community/tm4c-hal" 16 | documentation = "https://docs.rs/tm4c-hal" 17 | 18 | [dependencies] 19 | cortex-m = "0.7" 20 | nb = "1" 21 | 22 | [dependencies.embedded-hal] 23 | version = "0.2.2" 24 | features = ["unproven"] 25 | 26 | [dependencies.cast] 27 | version = "0.2.2" 28 | default-features = false 29 | -------------------------------------------------------------------------------- /tm4c-hal/README.md: -------------------------------------------------------------------------------- 1 | # `tm4c-hal` 2 | 3 | > Common bits of HAL for the TM4C123x and TM4C129x family of microcontrollers 4 | 5 | You probably don't need this crate, you need `tm4c123x-hal` or `tm4c129x-hal` 6 | depending on your processor. 7 | 8 | ## Changelog 9 | 10 | ### Unreleased Changes ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/master/tm4c-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c-hal-0.4.2...master)) 11 | 12 | * Basic EEPROM Read, Write, Erase added 13 | * Updated dependencies in `tm4c123x`, `tm4c129x`, `tm4c123x-hal`, and 14 | `tm4c129x-hal` to use newer version of cortex-m (up to v0.7 as of this release). 15 | 16 | 17 | ### Unreleased Changes ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/master/tm4c-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c-hal-0.4.1...master)) 18 | 19 | * Implement use of sealed traits by downstream crates (i.e. `tm4c123x-hal` and `tm4c129x-hal`) 20 | 21 | ### v0.4.1 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c-hal-0.4.1/tm4c-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c-hal-0.4.1...tm4c-hal-0.4.0)) 22 | 23 | * I2C busy handling fixes 24 | 25 | ### v0.4.0 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c-hal-0.4.0/tm4c-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c-hal-0.4.0...tm4c-hal-0.3.0)) 26 | 27 | * I2C fixes 28 | 29 | ### v0.3.0 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c-hal-0.3.0/tm4c-hal)) 30 | 31 | * Changelog starts here 32 | 33 | ## License 34 | 35 | Licensed under either of 36 | 37 | - Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or 38 | http://www.apache.org/licenses/LICENSE-2.0) 39 | - MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) 40 | 41 | at your option. 42 | 43 | ### Contribution 44 | 45 | Unless you explicitly state otherwise, any contribution intentionally submitted 46 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 47 | dual licensed as above, without any additional terms or conditions. 48 | -------------------------------------------------------------------------------- /tm4c-hal/src/bb.rs: -------------------------------------------------------------------------------- 1 | //! Code to handle bit-banding. 2 | //! 3 | //! Bit-banding is where the SoC maps each 8-bit byte to 8 consecutive 32-bit 4 | //! words. Writing a 1 to that word sets the matching bit. Writing a 0 clears 5 | //! the matching bit. It means you can perform atomic bit set/clear; i.e. 6 | //! without a read-modify-write. 7 | 8 | use core::ptr::{read_volatile, write_volatile}; 9 | use cortex_m::asm::nop; 10 | 11 | /// Sets/Clears a bit at the given address atomically, using the bit-banding 12 | /// feature. 13 | /// 14 | /// # Safety 15 | /// 16 | /// We take a const pointer and mutate it, but that's because the 17 | /// svd2rust crate will only give us const pointers. 18 | pub unsafe fn change_bit(address: *const T, bit: u8, value: bool) { 19 | let address = address as u32; 20 | let bit_word = ref_to_bitband(address, bit); 21 | write_volatile(bit_word, if value { 0x01 } else { 0x00 }); 22 | } 23 | 24 | /// Sets and then Clears a bit at the given address atomically, using the bit- 25 | /// banding feature. 26 | /// 27 | /// # Safety 28 | /// 29 | /// We take a const pointer and mutate it, but that's because 30 | /// the svd2rust crate will only give us const pointers. 31 | pub unsafe fn toggle_bit(address: *const T, bit: u8) { 32 | let address = address as u32; 33 | let bit_word = ref_to_bitband(address, bit); 34 | write_volatile(bit_word, 0x01); 35 | write_volatile(bit_word, 0x00); 36 | } 37 | 38 | /// Spins while reading a bit at the given address atomically, using the bit- 39 | /// banding feature. We take a const pointer and mutate it, but that's because 40 | /// the svd2rust crate will only give us const pointers. 41 | pub fn spin_bit(address: *const T, bit: u8) { 42 | while !read_bit(address, bit) { 43 | nop(); 44 | } 45 | } 46 | 47 | /// Reads a bit at the given address atomically, using the bit-banding 48 | /// feature. 49 | pub fn read_bit(address: *const T, bit: u8) -> bool { 50 | let address = address as u32; 51 | let bit_word = ref_to_bitband(address, bit); 52 | unsafe { read_volatile(bit_word) != 0 } 53 | } 54 | 55 | /// Address must be >= 0x2000_0000 and <= 0x2007_FFFC. Bit must be < 32. 56 | fn ref_to_bitband(address: u32, bit: u8) -> *mut u32 { 57 | let prefix = address & 0xF000_0000; 58 | let byte_offset = address & 0x0FFF_FFFF; 59 | let bit_word_offset = (byte_offset * 32) + (u32::from(bit) * 4); 60 | let bit_word_addr = bit_word_offset + prefix + 0x0200_0000; 61 | bit_word_addr as *mut u32 62 | } 63 | -------------------------------------------------------------------------------- /tm4c-hal/src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Code for busy-waiting 2 | 3 | use crate::{sysctl::Clocks, time::Hertz}; 4 | use cortex_m::peripheral::{syst::SystClkSource, SYST}; 5 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 6 | 7 | /// System timer (SysTick) as a delay provider 8 | pub struct Delay { 9 | sysclk: Hertz, 10 | syst: SYST, 11 | } 12 | 13 | impl Delay { 14 | /// Configures the system timer (SysTick) as a delay provider 15 | pub fn new(mut syst: SYST, clocks: &Clocks) -> Self { 16 | syst.set_clock_source(SystClkSource::Core); 17 | 18 | Delay { 19 | syst, 20 | sysclk: clocks.sysclk, 21 | } 22 | } 23 | 24 | /// Releases the system timer (SysTick) resource 25 | pub fn free(self) -> SYST { 26 | self.syst 27 | } 28 | } 29 | 30 | impl DelayMs for Delay { 31 | fn delay_ms(&mut self, ms: u32) { 32 | self.delay_us(ms * 1_000); 33 | } 34 | } 35 | 36 | impl DelayMs for Delay { 37 | fn delay_ms(&mut self, ms: u16) { 38 | self.delay_ms(cast::u32(ms)); 39 | } 40 | } 41 | 42 | impl DelayMs for Delay { 43 | fn delay_ms(&mut self, ms: u8) { 44 | self.delay_ms(cast::u32(ms)); 45 | } 46 | } 47 | 48 | impl DelayUs for Delay { 49 | fn delay_us(&mut self, us: u32) { 50 | // Tricky to get this to not overflow 51 | let mut rvr = us * (self.sysclk.0 / 1_000_000); 52 | rvr += (us * ((self.sysclk.0 % 1_000_000) / 1_000)) / 1_000; 53 | rvr += (us * (self.sysclk.0 % 1_000)) / 1_000_000; 54 | 55 | while rvr >= 1 << 24 { 56 | self.syst.set_reload((1 << 24) - 1); 57 | self.syst.clear_current(); 58 | self.syst.enable_counter(); 59 | while !self.syst.has_wrapped() {} 60 | self.syst.disable_counter(); 61 | rvr -= 1 << 24; 62 | } 63 | 64 | assert!(rvr < (1 << 24)); 65 | self.syst.set_reload(rvr); 66 | self.syst.clear_current(); 67 | self.syst.enable_counter(); 68 | while !self.syst.has_wrapped() {} 69 | self.syst.disable_counter(); 70 | } 71 | } 72 | 73 | impl DelayUs for Delay { 74 | fn delay_us(&mut self, us: u16) { 75 | self.delay_us(cast::u32(us)) 76 | } 77 | } 78 | 79 | impl DelayUs for Delay { 80 | fn delay_us(&mut self, us: u8) { 81 | self.delay_us(cast::u32(us)) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tm4c-hal/src/eeprom.rs: -------------------------------------------------------------------------------- 1 | //! Code for the EEProm module. 2 | //! 3 | //! Tested on a TM4C123 Tiva C Series Launchpad 4 | //! 5 | //! Note: This code manually increments the EEBLOCK and EEOFFSET registers 6 | //! after each read and write instead of using the EERDWRINC register. The 7 | //! debugger was giving inconsistent register results for the EEOFFSET register 8 | //! after using EERDWRINC. Also, the EERDWRINC does not increment the block in 9 | //! the case of a wrap of the offset, so it seems less useful for data that 10 | //! spans blocks. 11 | //! 12 | //! This flexibility comes at the cost of efficiency, as the 13 | //! datasheet calls for at least 4 cycles of delay after setting the EEBLOCK 14 | //! register. 15 | 16 | /// Possible errors for the Flash memory module 17 | #[derive(Debug, PartialEq)] 18 | pub enum EepromError { 19 | /// Eeprom is not finished 20 | Busy, 21 | /// Address is out of bounds 22 | AddressOutOfBounds, 23 | /// Block is out of bounds 24 | BlockOutOfBounds, 25 | /// Offset is out of bounds 26 | OffsetOutOfBounds, 27 | /// Indicates that writing data would exceed the EEPROM memory space 28 | WriteWouldOverflow, 29 | /// Indicates that reading data would exceed the EEPROM memory space 30 | ReadWouldOverflow, 31 | /// Requesting to read more data than the provided buffer can hold 32 | ReadBufferTooSmall, 33 | } 34 | 35 | impl core::fmt::Display for EepromError { 36 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 37 | match self { 38 | EepromError::Busy => write!(f, "Eeprom is busy"), 39 | EepromError::AddressOutOfBounds => write!(f, "Address is out of bounds"), 40 | EepromError::BlockOutOfBounds => write!(f, "Block is out of bounds"), 41 | EepromError::OffsetOutOfBounds => write!(f, "Offset is out of bounds"), 42 | EepromError::WriteWouldOverflow => { 43 | write!(f, "Writing this data would overflow the EEPROM") 44 | } 45 | EepromError::ReadWouldOverflow => { 46 | write!(f, "Reading this data would overflow the EEPROM") 47 | } 48 | EepromError::ReadBufferTooSmall => write!(f, "Allocated buffer too small for reading"), 49 | } 50 | } 51 | } 52 | 53 | /// Struct used to pack the block and offset 54 | #[derive(Clone, Copy)] 55 | pub struct EepromAddress { 56 | /// Eeprom block 57 | block: usize, 58 | /// Eeprom offset in a block 59 | offset: usize, 60 | } 61 | 62 | impl EepromAddress { 63 | /// Creates a new EepromAddres with configured block and offset 64 | pub fn new(block: usize, offset: usize) -> Self { 65 | EepromAddress { block, offset } 66 | } 67 | 68 | /// Returns the block 69 | pub fn block(&self) -> usize { 70 | self.block 71 | } 72 | 73 | /// Returns the offset 74 | pub fn offset(&self) -> usize { 75 | self.offset 76 | } 77 | 78 | /// Increments the offset by one, if that would cause an overflow, increment the block. If 79 | /// both the block and offset wrap, the output for the new block and offset 80 | /// will both be 0. 81 | pub fn increment(&mut self, offset_size: usize, block_size: usize) -> &mut Self { 82 | self.offset += 1; 83 | if self.offset >= offset_size { 84 | self.offset = 0; 85 | self.block += 1; 86 | if self.block >= block_size { 87 | self.block = 0; 88 | } 89 | } 90 | 91 | self 92 | } 93 | } 94 | 95 | /// Series of traits to make access blocks easier 96 | pub trait Blocks { 97 | /// Returns the blocksize for read / write to the flash 98 | fn block_size(&self) -> Result; 99 | 100 | /// Returns the EepromAddress for a given index. Valid indexes are 0 to 101 | /// EEPROM_END_ADDRESS_WORDS. 102 | fn word_index_to_address(&self, index: usize) -> Result; 103 | 104 | /// Gives the the word index (0 to EEPROM_END_ADDRESS_WORDS) for a 105 | /// given EepromAddress 106 | fn address_to_word_index(&self, block: &EepromAddress) -> Result; 107 | } 108 | 109 | /// Erase functions of the EEPROM 110 | pub trait Erase { 111 | /// Erase (zero out) data starting at an address spanning a length of bytes (not words!) 112 | fn erase(&mut self, address: &EepromAddress, length_bytes: usize) -> Result<(), EepromError>; 113 | 114 | /// Erase (zero out) a block 115 | fn erase_block(&mut self, block: usize) -> Result<(), EepromError>; 116 | } 117 | 118 | /// Check if the Eeprom is busy 119 | pub trait Busy { 120 | /// Check the EEDONE register, true if busy 121 | fn is_busy(&self) -> bool; 122 | 123 | /// Blocks until the EEPROM is not busy 124 | fn wait(&self); 125 | } 126 | 127 | /// Write data to the EEPROM 128 | pub trait Write { 129 | /// Write data to a flash address 130 | fn write(&mut self, address: &EepromAddress, data: &[u8]) -> Result<(), EepromError>; 131 | } 132 | 133 | /// Read data from the EEPROM 134 | pub trait Read { 135 | /// Eeprom Address to start reading data from 136 | fn read( 137 | &mut self, 138 | address: &EepromAddress, 139 | bytes_to_read: usize, 140 | buffer: &mut [u8], 141 | ) -> Result<(), EepromError>; 142 | } 143 | -------------------------------------------------------------------------------- /tm4c-hal/src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! Code for GPIO pins 2 | 3 | use core::marker::PhantomData; 4 | 5 | /// All unlocked pin modes implement this 6 | pub trait IsUnlocked {} 7 | 8 | /// All input modes implement this 9 | pub trait InputMode {} 10 | 11 | /// All output modes implement this 12 | pub trait OutputMode {} 13 | 14 | /// OpenDrain modes implement this 15 | pub trait OpenDrainMode { 16 | /// Is pull-up enabled 17 | fn pup() -> bool; 18 | } 19 | 20 | /// All the different Alternate Functions you can choose implement this 21 | pub trait AlternateFunctionChoice { 22 | /// Which Alternate Function (numbered 1 through 15) is this? 23 | fn number() -> u32; 24 | } 25 | 26 | /// Input mode (type state) 27 | pub struct Input 28 | where 29 | MODE: InputMode, 30 | { 31 | _mode: PhantomData, 32 | } 33 | impl IsUnlocked for Input where MODE: InputMode {} 34 | 35 | /// Sub-mode of Input: Floating input (type state) 36 | pub struct Floating; 37 | impl InputMode for Floating {} 38 | impl OpenDrainMode for Floating { 39 | /// Pull-up is not enabled 40 | fn pup() -> bool { 41 | false 42 | } 43 | } 44 | 45 | /// Sub-mode of Input: Pulled down input (type state) 46 | pub struct PullDown; 47 | impl InputMode for PullDown {} 48 | 49 | /// Sub-mode of Input: Pulled up input (type state) 50 | pub struct PullUp; 51 | impl InputMode for PullUp {} 52 | impl OpenDrainMode for PullUp { 53 | /// Pull-up is enabled 54 | fn pup() -> bool { 55 | true 56 | } 57 | } 58 | 59 | /// Tri-state 60 | pub struct Tristate; 61 | impl IsUnlocked for Tristate {} 62 | 63 | /// Output mode (type state) 64 | pub struct Output 65 | where 66 | MODE: OutputMode, 67 | { 68 | _mode: PhantomData, 69 | } 70 | impl IsUnlocked for Output where MODE: OutputMode {} 71 | 72 | /// AlternateFunction mode (type state for a GPIO pin) 73 | pub struct AlternateFunction 74 | where 75 | AF: AlternateFunctionChoice, 76 | MODE: OutputMode, 77 | { 78 | _func: PhantomData, 79 | _mode: PhantomData, 80 | } 81 | impl IsUnlocked for AlternateFunction 82 | where 83 | AF: AlternateFunctionChoice, 84 | MODE: OutputMode, 85 | { 86 | } 87 | 88 | /// Sub-mode of Output/AlternateFunction: Push pull output (type state for 89 | /// Output) 90 | pub struct PushPull; 91 | impl OutputMode for PushPull {} 92 | impl OutputMode for PullDown {} 93 | impl OutputMode for PullUp {} 94 | 95 | /// Sub-mode of Output/AlternateFunction: Open drain output (type state for 96 | /// Output) 97 | pub struct OpenDrain 98 | where 99 | ODM: OpenDrainMode, 100 | { 101 | _pull: PhantomData, 102 | } 103 | impl OutputMode for OpenDrain where ODM: OpenDrainMode {} 104 | 105 | /// Alternate function 1 (type state) 106 | pub struct AF1; 107 | impl AlternateFunctionChoice for AF1 { 108 | fn number() -> u32 { 109 | 1 110 | } 111 | } 112 | 113 | /// Alternate function 2 (type state) 114 | pub struct AF2; 115 | impl AlternateFunctionChoice for AF2 { 116 | fn number() -> u32 { 117 | 2 118 | } 119 | } 120 | 121 | /// Alternate function 3 (type state) 122 | pub struct AF3; 123 | impl AlternateFunctionChoice for AF3 { 124 | fn number() -> u32 { 125 | 3 126 | } 127 | } 128 | 129 | /// Alternate function 4 (type state) 130 | pub struct AF4; 131 | impl AlternateFunctionChoice for AF4 { 132 | fn number() -> u32 { 133 | 4 134 | } 135 | } 136 | 137 | /// Alternate function 5 (type state) 138 | pub struct AF5; 139 | impl AlternateFunctionChoice for AF5 { 140 | fn number() -> u32 { 141 | 5 142 | } 143 | } 144 | 145 | /// Alternate function 6 (type state) 146 | pub struct AF6; 147 | impl AlternateFunctionChoice for AF6 { 148 | fn number() -> u32 { 149 | 6 150 | } 151 | } 152 | 153 | /// Alternate function 7 (type state) 154 | pub struct AF7; 155 | impl AlternateFunctionChoice for AF7 { 156 | fn number() -> u32 { 157 | 7 158 | } 159 | } 160 | 161 | /// Alternate function 8 (type state) 162 | pub struct AF8; 163 | impl AlternateFunctionChoice for AF8 { 164 | fn number() -> u32 { 165 | 8 166 | } 167 | } 168 | 169 | /// Alternate function 9 (type state) 170 | pub struct AF9; 171 | impl AlternateFunctionChoice for AF9 { 172 | fn number() -> u32 { 173 | 9 174 | } 175 | } 176 | 177 | // 10 through 13 are not available on this chip. 178 | 179 | /// Alternate function 14 (type state) 180 | pub struct AF14; 181 | impl AlternateFunctionChoice for AF14 { 182 | fn number() -> u32 { 183 | 14 184 | } 185 | } 186 | 187 | /// Pin is locked through the GPIOCR register 188 | pub struct Locked; 189 | 190 | /// Sets when a GPIO pin triggers an interrupt. 191 | pub enum InterruptMode { 192 | /// Interrupt when level is low 193 | LevelLow, 194 | /// Interrupt when level is high 195 | LevelHigh, 196 | /// Interrupt on rising edge 197 | EdgeRising, 198 | /// Interrupt on falling edge 199 | EdgeFalling, 200 | /// Interrupt on both rising and falling edges 201 | EdgeBoth, 202 | /// Disable interrupts on this pin 203 | Disabled, 204 | } 205 | 206 | // End of file 207 | -------------------------------------------------------------------------------- /tm4c-hal/src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Common I2C code for TM4C123 and TM4C129 2 | 3 | /// I2C error 4 | #[derive(Debug)] 5 | #[non_exhaustive] 6 | pub enum Error { 7 | /// Bus Busy 8 | BusBusy, 9 | 10 | /// Arbitration loss 11 | Arbitration, 12 | 13 | /// Missing Data ACK 14 | DataAck, 15 | 16 | /// Missing Address ACK 17 | AdrAck, 18 | 19 | /// I2C Timeout 20 | Timeout, 21 | } 22 | 23 | #[macro_export] 24 | /// Implements the traits for an I2C peripheral 25 | macro_rules! i2c_pins { 26 | ($I2Cn:ident, 27 | scl: [$(($($sclgpio: ident)::*, $sclaf: ident)),*], 28 | sda: [$(($($sdagpio: ident)::*, $sdaaf: ident)),*], 29 | ) => { 30 | $( 31 | impl SclPin<$I2Cn> for $($sclgpio)::*> 32 | where 33 | T: OutputMode, 34 | {} 35 | )* 36 | 37 | $( 38 | impl SdaPin<$I2Cn> for $($sdagpio)::*> 39 | where 40 | T: OutputMode, 41 | {} 42 | )* 43 | } 44 | } 45 | 46 | #[macro_export] 47 | /// Spins until the controler is ready (mcs.busy is clear) and optionally on 48 | /// another field of the mcs register until it is clear or set (depending on op 49 | /// parameter). 50 | macro_rules! i2c_busy_wait { 51 | ($i2c:expr $(, $field:ident, $op:ident)? ) => {{ 52 | // in 'release' builds, the time between setting the `run` bit and checking the `busy` 53 | // bit is too short and the `busy` bit is not reliably set by the time you get there, 54 | // it can take up to 8 clock cycles for the `run` to begin so this delay allows time 55 | // for that hardware synchronization 56 | delay(8); 57 | 58 | // Allow 1,000 clock cycles before we timeout. At 100 kHz, this is 10 ms. 59 | $i2c.mclkocnt 60 | .write(|w| unsafe { w.cntl().bits((1_000 >> 4) as u8) }); 61 | 62 | let mcs = loop { 63 | let mcs = $i2c.mcs.read(); 64 | 65 | if mcs.busy().bit_is_clear() { 66 | break mcs; 67 | } 68 | }; 69 | 70 | 71 | if mcs.clkto().bit_is_set() { 72 | return Err(Error::Timeout) 73 | } else if mcs.arblst().bit_is_set() { 74 | return Err(Error::Arbitration) 75 | } else if mcs.error().bit_is_set() { 76 | if mcs.adrack().bit_is_set() { 77 | return Err(Error::AdrAck); 78 | } else { // if mcs.datack().bit_is_set() { 79 | return Err(Error::DataAck); 80 | } 81 | } 82 | 83 | $( loop { 84 | if mcs.clkto().bit_is_set() { 85 | return Err(Error::Timeout) 86 | } else if mcs.arblst().bit_is_set() { 87 | return Err(Error::Arbitration) 88 | } else if mcs.error().bit_is_set() { 89 | if mcs.adrack().bit_is_set() { 90 | return Err(Error::AdrAck); 91 | } else { // if mcs.datack().bit_is_set() { 92 | return Err(Error::DataAck); 93 | } 94 | } else if mcs.$field().$op() { 95 | break; 96 | } else { 97 | // try again 98 | } 99 | };)? 100 | 101 | Ok(()) 102 | }}; 103 | } 104 | 105 | #[macro_export] 106 | /// Implements embedded-hal for an TM4C I2C peripheral 107 | macro_rules! i2c_hal { 108 | ($($I2CX:ident: ($powerDomain:ident, $i2cX:ident),)+) => { 109 | $( 110 | impl I2c<$I2CX, (SCL, SDA)> { 111 | /// Configures the I2C peripheral to work in master mode 112 | pub fn $i2cX( 113 | i2c: $I2CX, 114 | pins: (SCL, SDA), 115 | freq: F, 116 | clocks: &Clocks, 117 | pc: &sysctl::PowerControl, 118 | ) -> Self where 119 | F: Into, 120 | SCL: SclPin<$I2CX>, 121 | SDA: SdaPin<$I2CX>, 122 | { 123 | sysctl::control_power( 124 | pc, sysctl::Domain::$powerDomain, 125 | sysctl::RunMode::Run, sysctl::PowerState::On); 126 | sysctl::reset(pc, sysctl::Domain::$powerDomain); 127 | 128 | // set Master Function Enable, and clear other bits. 129 | i2c.mcr.write(|w| w.mfe().set_bit()); 130 | 131 | // Write TimerPeriod configuration and clear other bits. 132 | let freq = freq.into().0; 133 | let tpr = ((clocks.sysclk.0/(2*10*freq))-1) as u8; 134 | 135 | i2c.mtpr.write(|w| unsafe {w.tpr().bits(tpr)}); 136 | 137 | I2c { i2c, pins } 138 | } 139 | 140 | /// Releases the I2C peripheral and associated pins 141 | pub fn free(self) -> ($I2CX, (SCL, SDA)) { 142 | (self.i2c, self.pins) 143 | } 144 | } 145 | 146 | impl Write for I2c<$I2CX, PINS> { 147 | type Error = Error; 148 | 149 | fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { 150 | // Write Slave address and clear Receive bit 151 | self.i2c.msa.write(|w| unsafe { 152 | w.sa().bits(addr) 153 | }); 154 | 155 | // Put first byte in data register 156 | self.i2c.mdr.write(|w| unsafe { 157 | w.data().bits(bytes[0]) 158 | }); 159 | 160 | let sz = bytes.len(); 161 | 162 | i2c_busy_wait!(self.i2c, busbsy, bit_is_clear)?; 163 | 164 | // Send START + RUN 165 | // If single byte transfer, set STOP 166 | self.i2c.mcs.write(|w| { 167 | if sz == 1 { 168 | w.stop().set_bit(); 169 | } 170 | w.start().set_bit() 171 | .run().set_bit() 172 | }); 173 | 174 | for (i,byte) in (&bytes[1..]).iter().enumerate() { 175 | i2c_busy_wait!(self.i2c)?; 176 | 177 | // Put next byte in data register 178 | self.i2c.mdr.write(|w| unsafe { 179 | w.data().bits(*byte) 180 | }); 181 | 182 | // Send RUN command (Burst continue) 183 | // Set STOP on last byte 184 | self.i2c.mcs.write(|w| { 185 | if (i+1) == (sz-1) { 186 | w.stop().set_bit(); 187 | } 188 | w.run().set_bit() 189 | }); 190 | } 191 | 192 | i2c_busy_wait!(self.i2c)?; 193 | 194 | Ok(()) 195 | } 196 | } 197 | 198 | impl Read for I2c<$I2CX, PINS> { 199 | type Error = Error; 200 | 201 | fn read( 202 | &mut self, 203 | addr: u8, 204 | buffer: &mut [u8], 205 | ) -> Result<(), Error> { 206 | 207 | // Write Slave address and set Receive bit 208 | self.i2c.msa.write(|w| unsafe { 209 | w.sa().bits(addr) 210 | .rs().set_bit() 211 | }); 212 | 213 | i2c_busy_wait!(self.i2c, busbsy, bit_is_clear)?; 214 | 215 | let recv_sz = buffer.len(); 216 | 217 | if recv_sz == 1 { 218 | // Single receive 219 | self.i2c.mcs.write(|w| { 220 | w.run().set_bit() 221 | .start().set_bit() 222 | .stop().set_bit() 223 | }); 224 | 225 | i2c_busy_wait!(self.i2c)?; 226 | buffer[0] = self.i2c.mdr.read().data().bits(); 227 | } else { 228 | self.i2c.mcs.write(|w| { 229 | w.start().set_bit() 230 | .run().set_bit() 231 | .ack().set_bit() 232 | }); 233 | 234 | i2c_busy_wait!(self.i2c)?; 235 | buffer[0] = self.i2c.mdr.read().data().bits(); 236 | 237 | for byte in &mut buffer[1..recv_sz-1] { 238 | self.i2c.mcs.write(|w| { 239 | w.run().set_bit() 240 | .ack().set_bit() 241 | }); 242 | i2c_busy_wait!(self.i2c)?; 243 | *byte = self.i2c.mdr.read().data().bits(); 244 | } 245 | self.i2c.mcs.write(|w| { 246 | w.run().set_bit() 247 | .stop().set_bit() 248 | }); 249 | 250 | i2c_busy_wait!(self.i2c)?; 251 | buffer[recv_sz-1] = self.i2c.mdr.read().data().bits(); 252 | } 253 | 254 | Ok(()) 255 | } 256 | } 257 | 258 | impl WriteRead for I2c<$I2CX, PINS> { 259 | type Error = Error; 260 | 261 | fn write_read( 262 | &mut self, 263 | addr: u8, 264 | bytes: &[u8], 265 | buffer: &mut [u8], 266 | ) -> Result<(), Error> { 267 | 268 | let write_len = bytes.len(); 269 | 270 | if buffer.len() == 0 { 271 | return self.write(addr, bytes); 272 | } 273 | 274 | if bytes.len() == 0 { 275 | return self.read(addr, buffer); 276 | } 277 | 278 | // Write Slave address and clear Receive bit 279 | self.i2c.msa.write(|w| unsafe { 280 | w.sa().bits(addr) 281 | }); 282 | 283 | // send first byte 284 | self.i2c.mdr.write(|w| unsafe { 285 | w.data().bits(bytes[0]) 286 | }); 287 | 288 | i2c_busy_wait!(self.i2c, busbsy, bit_is_clear)?; 289 | self.i2c.mcs.write(|w| { 290 | w.start().set_bit() 291 | .run().set_bit() 292 | }); 293 | 294 | i2c_busy_wait!(self.i2c)?; 295 | for byte in (&bytes[1..write_len]).iter() { 296 | self.i2c.mdr.write(|w| unsafe { 297 | w.data().bits(*byte) 298 | }); 299 | 300 | self.i2c.mcs.write(|w| { 301 | w.run().set_bit() 302 | }); 303 | 304 | i2c_busy_wait!(self.i2c)?; 305 | } 306 | 307 | // Write Slave address and set Receive bit 308 | self.i2c.msa.write(|w| unsafe { 309 | w.sa().bits(addr) 310 | .rs().set_bit() 311 | }); 312 | 313 | let recv_sz = buffer.len(); 314 | 315 | if recv_sz == 1 { 316 | // emit Repeated START and STOP for single receive 317 | self.i2c.mcs.write(|w| { 318 | w.run().set_bit() 319 | .start().set_bit() 320 | .stop().set_bit() 321 | }); 322 | 323 | i2c_busy_wait!(self.i2c)?; 324 | buffer[0] = self.i2c.mdr.read().data().bits(); 325 | } else { 326 | // emit Repeated START 327 | self.i2c.mcs.write(|w| { 328 | w.run().set_bit() 329 | .start().set_bit() 330 | .ack().set_bit() 331 | }); 332 | 333 | i2c_busy_wait!(self.i2c)?; 334 | buffer[0] = self.i2c.mdr.read().data().bits(); 335 | 336 | for byte in &mut buffer[1..recv_sz-1] { 337 | self.i2c.mcs.write(|w| { 338 | w.run().set_bit() 339 | .ack().set_bit() 340 | }); 341 | i2c_busy_wait!(self.i2c)?; 342 | *byte = self.i2c.mdr.read().data().bits(); 343 | } 344 | 345 | self.i2c.mcs.write(|w| { 346 | w.run().set_bit() 347 | .stop().set_bit() 348 | }); 349 | 350 | i2c_busy_wait!(self.i2c)?; 351 | buffer[recv_sz-1] = self.i2c.mdr.read().data().bits(); 352 | } 353 | 354 | Ok(()) 355 | } 356 | } 357 | )+ 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /tm4c-hal/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Generic implementation code for both TM4C123 and TM4C129. 2 | 3 | #![no_std] 4 | #![deny(missing_docs, warnings)] 5 | #![allow(deprecated)] 6 | 7 | pub mod bb; 8 | pub mod delay; 9 | pub mod eeprom; 10 | pub mod gpio; 11 | pub mod i2c; 12 | pub mod serial; 13 | pub mod sysctl; 14 | pub mod time; 15 | 16 | ///! An internal macro to implement the GPIO functionality for each port 17 | #[macro_export] 18 | macro_rules! gpio_macro { 19 | ($chip_crate:ident, $GPIOX:ident, $gpiox:ident, $iopd:ident, $PXx:ident, [ 20 | $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ 21 | ]) => { 22 | /// GPIO 23 | pub mod $gpiox { 24 | use super::*; 25 | use $chip_crate::$GPIOX; 26 | 27 | /// Provides mutual-exclusion for certain GPIO operations (such as 28 | /// selecting an alternate mode) that can't be done atomically. 29 | pub struct GpioControl { 30 | _0: (), 31 | } 32 | 33 | /// GPIO parts 34 | pub struct Parts { 35 | /// Pass an &mut reference to methods that require it. 36 | pub control: GpioControl, 37 | $( 38 | /// Pin 39 | pub $pxi: $PXi<$MODE>, 40 | )+ 41 | } 42 | 43 | impl GpioExt for $GPIOX { 44 | type Parts = Parts; 45 | 46 | /// Break this GPIO port into separate pins 47 | fn split(self, pc: &sysctl::PowerControl) -> Parts { 48 | sysctl::control_power( 49 | pc, sysctl::Domain::$iopd, 50 | sysctl::RunMode::Run, sysctl::PowerState::On); 51 | sysctl::reset(pc, sysctl::Domain::$iopd); 52 | 53 | Parts { 54 | control: GpioControl { _0: () }, 55 | $( 56 | $pxi: $PXi { _mode: PhantomData }, 57 | )+ 58 | } 59 | } 60 | } 61 | 62 | /// Partially erased pin 63 | pub struct $PXx { 64 | i: u8, 65 | _mode: PhantomData, 66 | } 67 | 68 | impl StatefulOutputPin for $PXx> where MODE: OutputMode { 69 | fn is_set_high(&self) -> bool { 70 | let p = unsafe { &*$GPIOX::ptr() }; 71 | bb::read_bit(&p.data, self.i) 72 | } 73 | 74 | fn is_set_low(&self) -> bool { 75 | !self.is_set_high() 76 | } 77 | } 78 | 79 | impl OutputPin for $PXx> where MODE: OutputMode { 80 | fn set_high(&mut self) { 81 | let p = unsafe { &*$GPIOX::ptr() }; 82 | unsafe { bb::change_bit(&p.data, self.i, true); } 83 | } 84 | 85 | fn set_low(&mut self) { 86 | let p = unsafe { &*$GPIOX::ptr() }; 87 | unsafe { bb::change_bit(&p.data, self.i, false); } 88 | } 89 | } 90 | 91 | impl InputPin for $PXx> where MODE: InputMode { 92 | fn is_high(&self) -> bool { 93 | let p = unsafe { &*$GPIOX::ptr() }; 94 | bb::read_bit(&p.data, self.i) 95 | } 96 | 97 | fn is_low(&self) -> bool { 98 | !self.is_high() 99 | } 100 | } 101 | 102 | impl $PXx> where MODE: InputMode { 103 | /// Enables or disables interrupts on this GPIO pin. 104 | pub fn set_interrupt_mode(&mut self, mode: InterruptMode) { 105 | let p = unsafe { &*$GPIOX::ptr() }; 106 | unsafe { bb::change_bit(&p.im, self.i, false); } 107 | match mode { 108 | InterruptMode::LevelHigh => { 109 | // IM &= ~self.i; 110 | unsafe { bb::change_bit(&p.im, self.i, false); } 111 | // IS |= self.i; 112 | unsafe { bb::change_bit(&p.is, self.i, true); } 113 | // IBE &= ~self.i; 114 | unsafe { bb::change_bit(&p.ibe, self.i, false); } 115 | // IEV |= self.i; 116 | unsafe { bb::change_bit(&p.iev, self.i, true); } 117 | // IM |= self.i; 118 | unsafe { bb::change_bit(&p.im, self.i, true); } 119 | }, 120 | InterruptMode::LevelLow => { 121 | // IM &= ~self.i; 122 | unsafe { bb::change_bit(&p.im, self.i, false); } 123 | // IS |= self.i; 124 | unsafe { bb::change_bit(&p.is, self.i, true); } 125 | // IBE &= ~self.i; 126 | unsafe { bb::change_bit(&p.ibe, self.i, false); } 127 | // IEV &= ~self.i; 128 | unsafe { bb::change_bit(&p.iev, self.i, false); } 129 | // IM |= self.i; 130 | unsafe { bb::change_bit(&p.im, self.i, true); } 131 | }, 132 | InterruptMode::EdgeRising => { 133 | // IM &= ~self.i; 134 | unsafe { bb::change_bit(&p.im, self.i, false); } 135 | // IS &= ~self.i; 136 | unsafe { bb::change_bit(&p.is, self.i, false); } 137 | // IBE &= ~self.i; 138 | unsafe { bb::change_bit(&p.ibe, self.i, false); } 139 | // IEV |= self.i; 140 | unsafe { bb::change_bit(&p.iev, self.i, true); } 141 | // IM |= self.i; 142 | unsafe { bb::change_bit(&p.im, self.i, true); } 143 | }, 144 | InterruptMode::EdgeFalling => { 145 | // IM &= ~self.i; 146 | unsafe { bb::change_bit(&p.im, self.i, false); } 147 | // IS &= ~self.i; 148 | unsafe { bb::change_bit(&p.is, self.i, false); } 149 | // IBE &= ~self.i; 150 | unsafe { bb::change_bit(&p.ibe, self.i, false); } 151 | // IEV &= ~self.i; 152 | unsafe { bb::change_bit(&p.iev, self.i, false); } 153 | // IM |= self.i; 154 | unsafe { bb::change_bit(&p.im, self.i, true); } 155 | }, 156 | InterruptMode::EdgeBoth => { 157 | // IM &= ~self.i; 158 | unsafe { bb::change_bit(&p.im, self.i, false); } 159 | // IS &= ~self.i; 160 | unsafe { bb::change_bit(&p.is, self.i, false); } 161 | // IBE |= self.i; 162 | unsafe { bb::change_bit(&p.ibe, self.i, true); } 163 | // IEV |= self.i; 164 | unsafe { bb::change_bit(&p.iev, self.i, true); } 165 | // IM |= self.i; 166 | unsafe { bb::change_bit(&p.im, self.i, true); } 167 | }, 168 | InterruptMode::Disabled => { 169 | // IM &= ~self.i; 170 | unsafe { bb::change_bit(&p.im, self.i, false); } 171 | }, 172 | } 173 | } 174 | 175 | /// Returns the current interrupt status for this pin. 176 | pub fn get_interrupt_status(&self) -> bool { 177 | let p = unsafe { &*$GPIOX::ptr() }; 178 | bb::read_bit(&p.mis, self.i) 179 | } 180 | 181 | /// Marks the interrupt for this pin as handled. You should 182 | /// call this (or perform its functionality) from the ISR. 183 | pub fn clear_interrupt(&self) { 184 | let p = unsafe { &*$GPIOX::ptr() }; 185 | unsafe { bb::change_bit(&p.icr, self.i, true); } 186 | } 187 | } 188 | 189 | $( 190 | /// Pin 191 | pub struct $PXi { 192 | _mode: PhantomData, 193 | } 194 | 195 | impl crate::Sealed for $PXi {} 196 | 197 | impl $PXi where MODE: IsUnlocked { 198 | /// Configures the pin to serve as alternate function 1 through 15. 199 | /// Disables open-drain to make the output a push-pull. 200 | pub fn into_af_push_pull( 201 | self, 202 | _gpio_control: &mut GpioControl, 203 | ) -> $PXi> where AF: AlternateFunctionChoice { 204 | let p = unsafe { &*$GPIOX::ptr() }; 205 | let mask = 0xF << ($i * 4); 206 | let bits = AF::number() << ($i * 4); 207 | unsafe { 208 | p.pctl.modify(|r, w| w.bits((r.bits() & !mask) | bits)); 209 | } 210 | unsafe { bb::change_bit(&p.afsel, $i, true); } 211 | unsafe { bb::change_bit(&p.dir, $i, false); } 212 | unsafe { bb::change_bit(&p.odr, $i, false); } 213 | unsafe { bb::change_bit(&p.pur, $i, false); } 214 | unsafe { bb::change_bit(&p.pdr, $i, false); } 215 | unsafe { bb::change_bit(&p.den, $i, true); } 216 | $PXi { _mode: PhantomData } 217 | } 218 | 219 | /// Configures the pin to serve as alternate function 1 through 15 with 220 | /// a weak pull-up resistor. 221 | pub fn into_af_pull_up( 222 | self, 223 | _gpio_control: &mut GpioControl, 224 | ) -> $PXi> where AF: AlternateFunctionChoice { 225 | let p = unsafe { &*$GPIOX::ptr() }; 226 | let mask = 0xF << ($i * 4); 227 | let bits = AF::number() << ($i * 4); 228 | unsafe { 229 | p.pctl.modify(|r, w| w.bits((r.bits() & !mask) | bits)); 230 | } 231 | unsafe { bb::change_bit(&p.afsel, $i, true); } 232 | unsafe { bb::change_bit(&p.dir, $i, false); } 233 | unsafe { bb::change_bit(&p.odr, $i, false); } 234 | unsafe { bb::change_bit(&p.pur, $i, true); } 235 | unsafe { bb::change_bit(&p.pdr, $i, false); } 236 | unsafe { bb::change_bit(&p.den, $i, true); } 237 | $PXi { _mode: PhantomData } 238 | } 239 | 240 | /// Configures the pin to serve as alternate function 1 through 15 with 241 | /// a weak pull-down resistor. 242 | pub fn into_af_pull_down( 243 | self, 244 | _gpio_control: &mut GpioControl, 245 | ) -> $PXi> where AF: AlternateFunctionChoice { 246 | let p = unsafe { &*$GPIOX::ptr() }; 247 | let mask = 0xF << ($i * 4); 248 | let bits = AF::number() << ($i * 4); 249 | unsafe { 250 | p.pctl.modify(|r, w| w.bits((r.bits() & !mask) | bits)); 251 | } 252 | unsafe { bb::change_bit(&p.afsel, $i, true); } 253 | unsafe { bb::change_bit(&p.dir, $i, false); } 254 | unsafe { bb::change_bit(&p.odr, $i, false); } 255 | unsafe { bb::change_bit(&p.pur, $i, false); } 256 | unsafe { bb::change_bit(&p.pdr, $i, true); } 257 | unsafe { bb::change_bit(&p.den, $i, true); } 258 | $PXi { _mode: PhantomData } 259 | } 260 | 261 | /// Configures the pin to serve as alternate function 1 through 15. 262 | /// Enables open-drain (useful for I2C SDA, for example). 263 | pub fn into_af_open_drain( 264 | self, 265 | _gpio_control: &mut GpioControl, 266 | ) -> $PXi>> where AF: AlternateFunctionChoice, ODM: OpenDrainMode { 267 | let p = unsafe { &*$GPIOX::ptr() }; 268 | let mask = 0xF << ($i * 4); 269 | let bits = AF::number() << ($i * 4); 270 | unsafe { 271 | p.pctl.modify(|r, w| w.bits((r.bits() & !mask) | bits)); 272 | } 273 | unsafe { bb::change_bit(&p.afsel, $i, true); } 274 | unsafe { bb::change_bit(&p.dir, $i, false); } 275 | unsafe { bb::change_bit(&p.odr, $i, true); } 276 | unsafe { bb::change_bit(&p.pur, $i, ODM::pup()); } 277 | unsafe { bb::change_bit(&p.pdr, $i, false); } 278 | unsafe { bb::change_bit(&p.den, $i, true); } 279 | $PXi { _mode: PhantomData } 280 | } 281 | 282 | /// Configures the pin to operate as a floating input pin 283 | pub fn into_floating_input( 284 | self 285 | ) -> $PXi> { 286 | let p = unsafe { &*$GPIOX::ptr() }; 287 | unsafe { bb::change_bit(&p.afsel, $i, false); } 288 | unsafe { bb::change_bit(&p.dir, $i, false); } 289 | unsafe { bb::change_bit(&p.odr, $i, false); } 290 | unsafe { bb::change_bit(&p.pur, $i, false); } 291 | unsafe { bb::change_bit(&p.pdr, $i, false); } 292 | unsafe { bb::change_bit(&p.den, $i, true); } 293 | $PXi { _mode: PhantomData } 294 | } 295 | 296 | /// Configures the pin to operate as a pulled down input pin 297 | pub fn into_pull_down_input( 298 | self 299 | ) -> $PXi> { 300 | let p = unsafe { &*$GPIOX::ptr() }; 301 | unsafe { bb::change_bit(&p.afsel, $i, false); } 302 | unsafe { bb::change_bit(&p.dir, $i, false); } 303 | unsafe { bb::change_bit(&p.odr, $i, false); } 304 | unsafe { bb::change_bit(&p.pur, $i, false); } 305 | unsafe { bb::change_bit(&p.pdr, $i, true); } 306 | unsafe { bb::change_bit(&p.den, $i, true); } 307 | $PXi { _mode: PhantomData } 308 | } 309 | 310 | /// Configures the pin to operate as a pulled up input pin 311 | pub fn into_pull_up_input( 312 | self 313 | ) -> $PXi> { 314 | let p = unsafe { &*$GPIOX::ptr() }; 315 | unsafe { bb::change_bit(&p.afsel, $i, false); } 316 | unsafe { bb::change_bit(&p.dir, $i, false); } 317 | unsafe { bb::change_bit(&p.odr, $i, false); } 318 | unsafe { bb::change_bit(&p.pur, $i, true); } 319 | unsafe { bb::change_bit(&p.pdr, $i, false); } 320 | unsafe { bb::change_bit(&p.den, $i, true); } 321 | $PXi { _mode: PhantomData } 322 | } 323 | 324 | /// Configures the pin to operate as an open drain output pin 325 | pub fn into_open_drain_output( 326 | self 327 | ) -> $PXi>> where ODM: OpenDrainMode { 328 | let p = unsafe { &*$GPIOX::ptr() }; 329 | unsafe { bb::change_bit(&p.afsel, $i, false); } 330 | unsafe { bb::change_bit(&p.dir, $i, true); } 331 | unsafe { bb::change_bit(&p.odr, $i, true); } 332 | unsafe { bb::change_bit(&p.pur, $i, ODM::pup()); } 333 | unsafe { bb::change_bit(&p.pdr, $i, false); } 334 | unsafe { bb::change_bit(&p.den, $i, true); } 335 | $PXi { _mode: PhantomData } 336 | } 337 | 338 | /// Configures the pin to operate as an push pull output pin 339 | pub fn into_push_pull_output( 340 | self 341 | ) -> $PXi> { 342 | let p = unsafe { &*$GPIOX::ptr() }; 343 | unsafe { bb::change_bit(&p.afsel, $i, false); } 344 | unsafe { bb::change_bit(&p.dir, $i, true); } 345 | unsafe { bb::change_bit(&p.odr, $i, false); } 346 | unsafe { bb::change_bit(&p.pur, $i, false); } 347 | unsafe { bb::change_bit(&p.pdr, $i, false); } 348 | unsafe { bb::change_bit(&p.den, $i, true); } 349 | $PXi { _mode: PhantomData } 350 | } 351 | 352 | /// Configures the pin as tri-state 353 | pub fn into_tri_state( 354 | self 355 | ) -> $PXi { 356 | let p = unsafe { &*$GPIOX::ptr() }; 357 | unsafe { bb::change_bit(&p.den, $i, false); } 358 | unsafe { bb::change_bit(&p.afsel, $i, false); } 359 | unsafe { bb::change_bit(&p.dir, $i, false); } 360 | unsafe { bb::change_bit(&p.odr, $i, false); } 361 | unsafe { bb::change_bit(&p.pur, $i, false); } 362 | unsafe { bb::change_bit(&p.pdr, $i, false); } 363 | $PXi { _mode: PhantomData } 364 | } 365 | 366 | } 367 | 368 | impl $PXi { 369 | /// Erases the pin number from the type 370 | /// 371 | /// This is useful when you want to collect the pins into an array where you 372 | /// need all the elements to have the same type 373 | pub fn downgrade(self) -> $PXx { 374 | $PXx { 375 | i: $i, 376 | _mode: self._mode, 377 | } 378 | } 379 | } 380 | 381 | impl StatefulOutputPin for $PXi> where MODE: OutputMode { 382 | fn is_set_high(&self) -> bool { 383 | let p = unsafe { &*$GPIOX::ptr() }; 384 | bb::read_bit(&p.data, $i) 385 | } 386 | 387 | fn is_set_low(&self) -> bool { 388 | !self.is_set_high() 389 | } 390 | } 391 | 392 | impl OutputPin for $PXi> where MODE: OutputMode { 393 | fn set_high(&mut self) { 394 | let p = unsafe { &*$GPIOX::ptr() }; 395 | unsafe { bb::change_bit(&p.data, $i, true); } 396 | } 397 | 398 | fn set_low(&mut self) { 399 | let p = unsafe { &*$GPIOX::ptr() }; 400 | unsafe { bb::change_bit(&p.data, $i, false); } 401 | } 402 | } 403 | 404 | impl InputPin for $PXi> where MODE: InputMode { 405 | fn is_high(&self) -> bool { 406 | let p = unsafe { &*$GPIOX::ptr() }; 407 | bb::read_bit(&p.data, $i) 408 | } 409 | 410 | fn is_low(&self) -> bool { 411 | !self.is_high() 412 | } 413 | } 414 | 415 | impl $PXi> where MODE: InputMode { 416 | /// Enables or disables interrupts on this GPIO pin. 417 | pub fn set_interrupt_mode(&mut self, mode: InterruptMode) { 418 | let p = unsafe { &*$GPIOX::ptr() }; 419 | unsafe { bb::change_bit(&p.im, $i, false); } 420 | match mode { 421 | InterruptMode::LevelHigh => { 422 | // IM &= ~$i; 423 | unsafe { bb::change_bit(&p.im, $i, false); } 424 | // IS |= $i; 425 | unsafe { bb::change_bit(&p.is, $i, true); } 426 | // IBE &= ~$i; 427 | unsafe { bb::change_bit(&p.ibe, $i, false); } 428 | // IEV |= $i; 429 | unsafe { bb::change_bit(&p.iev, $i, true); } 430 | // IM |= $i; 431 | unsafe { bb::change_bit(&p.im, $i, true); } 432 | }, 433 | InterruptMode::LevelLow => { 434 | // IM &= ~$i; 435 | unsafe { bb::change_bit(&p.im, $i, false); } 436 | // IS |= $i; 437 | unsafe { bb::change_bit(&p.is, $i, true); } 438 | // IBE &= ~$i; 439 | unsafe { bb::change_bit(&p.ibe, $i, false); } 440 | // IEV &= ~$i; 441 | unsafe { bb::change_bit(&p.iev, $i, false); } 442 | // IM |= $i; 443 | unsafe { bb::change_bit(&p.im, $i, true); } 444 | }, 445 | InterruptMode::EdgeRising => { 446 | // IM &= ~$i; 447 | unsafe { bb::change_bit(&p.im, $i, false); } 448 | // IS &= ~$i; 449 | unsafe { bb::change_bit(&p.is, $i, false); } 450 | // IBE &= ~$i; 451 | unsafe { bb::change_bit(&p.ibe, $i, false); } 452 | // IEV |= $i; 453 | unsafe { bb::change_bit(&p.iev, $i, true); } 454 | // IM |= $i; 455 | unsafe { bb::change_bit(&p.im, $i, true); } 456 | }, 457 | InterruptMode::EdgeFalling => { 458 | // IM &= ~$i; 459 | unsafe { bb::change_bit(&p.im, $i, false); } 460 | // IS &= ~$i; 461 | unsafe { bb::change_bit(&p.is, $i, false); } 462 | // IBE &= ~$i; 463 | unsafe { bb::change_bit(&p.ibe, $i, false); } 464 | // IEV &= ~$i; 465 | unsafe { bb::change_bit(&p.iev, $i, false); } 466 | // IM |= $i; 467 | unsafe { bb::change_bit(&p.im, $i, true); } 468 | }, 469 | InterruptMode::EdgeBoth => { 470 | // IM &= ~$i; 471 | unsafe { bb::change_bit(&p.im, $i, false); } 472 | // IS &= ~$i; 473 | unsafe { bb::change_bit(&p.is, $i, false); } 474 | // IBE |= $i; 475 | unsafe { bb::change_bit(&p.ibe, $i, true); } 476 | // IEV |= $i; 477 | unsafe { bb::change_bit(&p.iev, $i, true); } 478 | // IM |= $i; 479 | unsafe { bb::change_bit(&p.im, $i, true); } 480 | }, 481 | InterruptMode::Disabled => { 482 | // IM &= ~$i; 483 | unsafe { bb::change_bit(&p.im, $i, false); } 484 | }, 485 | } 486 | } 487 | 488 | /// Returns the current interrupt status for this pin. 489 | pub fn get_interrupt_status(&self) -> bool { 490 | let p = unsafe { &*$GPIOX::ptr() }; 491 | bb::read_bit(&p.mis, $i) 492 | } 493 | 494 | /// Marks the interrupt for this pin as handled. You should 495 | /// call this (or perform its functionality) from the ISR. 496 | pub fn clear_interrupt(&self) { 497 | let p = unsafe { &*$GPIOX::ptr() }; 498 | unsafe { bb::change_bit(&p.icr, $i, true); } 499 | } 500 | } 501 | 502 | impl $PXi { 503 | /// Unlock a GPIO so that it can be used. This is required 504 | /// on 'special' GPIOs that the manufacturer doesn't want 505 | /// you to change by accident - like NMI and JTAG pins. 506 | pub fn unlock(self, _gpio_control: &mut GpioControl) -> $PXi { 507 | let p = unsafe { &*$GPIOX::ptr() }; 508 | p.lock.write(|w| w.lock().key()); 509 | p.cr.modify(|_, w| unsafe { w.bits(1 << $i) }); 510 | p.lock.write(|w| w.lock().unlocked()); 511 | unsafe { bb::change_bit(&p.den, $i, false); } 512 | unsafe { bb::change_bit(&p.afsel, $i, false); } 513 | unsafe { bb::change_bit(&p.dir, $i, false); } 514 | unsafe { bb::change_bit(&p.odr, $i, false); } 515 | unsafe { bb::change_bit(&p.pur, $i, false); } 516 | unsafe { bb::change_bit(&p.pdr, $i, false); } 517 | $PXi { _mode: PhantomData } 518 | } 519 | } 520 | )+ 521 | } 522 | } 523 | } 524 | 525 | ///! An internal macro to implement the UART functionality for each peripheral 526 | #[macro_export] 527 | macro_rules! uart_hal_macro { 528 | ($( 529 | $UARTX:ident: ($powerDomain:ident, $uartX:ident), 530 | )+) => { 531 | $crate::uart_traits_macro!(); 532 | 533 | $( 534 | impl Serial<$UARTX, TX, RX, RTS, CTS> { 535 | /// Configures a UART peripheral to provide serial communication 536 | pub fn $uartX( 537 | mut uart: $UARTX, 538 | tx_pin: TX, 539 | rx_pin: RX, 540 | mut rts_pin: RTS, 541 | mut cts_pin: CTS, 542 | baud_rate: Bps, 543 | nl_mode: NewlineMode, 544 | clocks: &Clocks, 545 | pc: &sysctl::PowerControl 546 | ) -> Self 547 | where 548 | TX: TxPin<$UARTX>, 549 | RX: RxPin<$UARTX>, 550 | CTS: CtsPin<$UARTX>, 551 | RTS: RtsPin<$UARTX>, 552 | { 553 | // Enable UART peripheral clocks 554 | sysctl::control_power( 555 | pc, sysctl::Domain::$powerDomain, 556 | sysctl::RunMode::Run, sysctl::PowerState::On); 557 | sysctl::reset(pc, sysctl::Domain::$powerDomain); 558 | 559 | // Reset UART 560 | uart.ctl.reset(); 561 | 562 | // Calculate baud rate dividers 563 | // baud_int = 64 * (sys_clk / (16 * baud)) 564 | // baud_int = 4 * (sys_clk / baud) 565 | // baud_int = ((8 * sys_clk) / baud) / 2, plus + 1 to round correctly 566 | let baud_int: u32 = (((clocks.sysclk.0 * 8) / baud_rate.0) + 1) / 2; 567 | 568 | // Set baud rate 569 | uart.ibrd.write(|w| 570 | unsafe { w.divint().bits((baud_int / 64) as u16) }); 571 | uart.fbrd.write(|w| 572 | unsafe { w.divfrac().bits((baud_int % 64) as u8) }); 573 | 574 | // Set data bits / parity / stop bits / enable fifo 575 | uart.lcrh.write(|w| w.wlen()._8().fen().bit(true)); 576 | 577 | // Activate flow control (if desired) 578 | rts_pin.enable(&mut uart); 579 | cts_pin.enable(&mut uart); 580 | 581 | // Enable uart 582 | uart.ctl.modify(|_, w| w.rxe().bit(true).txe().bit(true).uarten().bit(true)); 583 | 584 | Serial { uart, tx_pin, rx_pin, rts_pin, cts_pin, nl_mode } 585 | } 586 | 587 | /// Change the current baud rate for the UART. We need the 588 | /// `clocks` object in order to calculate the magic baud rate 589 | /// register values. 590 | pub fn change_baud_rate(&mut self, baud_rate: Bps, clocks: &Clocks) { 591 | // Stop UART 592 | self.uart.ctl.modify(|_, w| w.uarten().bit(false)); 593 | 594 | // Calculate baud rate dividers 595 | let baud_int: u32 = (((clocks.sysclk.0 * 8) / baud_rate.0) + 1) / 2; 596 | 597 | // Set baud rate 598 | self.uart.ibrd.write(|w| 599 | unsafe { w.divint().bits((baud_int / 64) as u16) }); 600 | self.uart.fbrd.write(|w| 601 | unsafe { w.divfrac().bits((baud_int % 64) as u8) }); 602 | 603 | // Set data bits / parity / stop bits / enable fifo 604 | // If you don't write to this register, the baud rate change doesn't take effect 605 | self.uart.lcrh.write(|w| w.wlen()._8().fen().bit(true)); 606 | 607 | // Start UART again 608 | self.uart.ctl.modify(|_, w| w.uarten().bit(true)); 609 | } 610 | 611 | /// Splits the `Serial` abstraction into a transmitter and a 612 | /// receiver half. If you do this you can transmit and receive 613 | /// in different threads. 614 | pub fn split(self) -> (Tx<$UARTX, TX, RTS>, Rx<$UARTX, RX, CTS>) { 615 | ( 616 | Tx { 617 | uart: self.uart, 618 | pin: self.tx_pin, 619 | nl_mode: self.nl_mode, 620 | flow_pin: self.rts_pin, 621 | }, 622 | Rx { 623 | _uart: PhantomData, 624 | pin: self.rx_pin, 625 | flow_pin: self.cts_pin, 626 | }, 627 | ) 628 | } 629 | 630 | /// Write a complete string to the UART. 631 | pub fn write_all(&mut self, data: &I) 632 | where 633 | I: AsRef<[u8]>, 634 | { 635 | for octet in data.as_ref().iter() { 636 | block!(self.write(*octet)).unwrap(); // E = Void 637 | } 638 | } 639 | 640 | /// Re-combine a split UART 641 | pub fn combine(tx: Tx<$UARTX, TX, RTS>, rx: Rx<$UARTX, RX, CTS>) -> Serial<$UARTX, TX, RX, RTS, CTS> { 642 | Serial { 643 | uart: tx.uart, 644 | nl_mode: tx.nl_mode, 645 | rx_pin: rx.pin, 646 | tx_pin: tx.pin, 647 | rts_pin: tx.flow_pin, 648 | cts_pin: rx.flow_pin, 649 | } 650 | } 651 | 652 | /// Releases the UART peripheral and associated pins 653 | pub fn free(self) -> ($UARTX, TX, RX, RTS, CTS) { 654 | (self.uart, self.tx_pin, self.rx_pin, self.rts_pin, self.cts_pin) 655 | } 656 | } 657 | 658 | impl Tx<$UARTX, TX, RTS> { 659 | /// Write a complete string to the UART. 660 | pub fn write_all(&mut self, data: &I) 661 | where 662 | I: AsRef<[u8]>, 663 | { 664 | for octet in data.as_ref().iter() { 665 | block!(self.write(*octet)).unwrap(); // E = Void 666 | } 667 | } 668 | } 669 | 670 | impl serial::Read for Serial<$UARTX, TX, RX, RTS, CTS> { 671 | type Error = Void; 672 | 673 | fn read(&mut self) -> nb::Result { 674 | if self.uart.fr.read().rxfe().bit() { 675 | return Err(nb::Error::WouldBlock); 676 | } 677 | Ok(self.uart.dr.read().data().bits()) 678 | } 679 | } 680 | 681 | impl serial::Read for Rx<$UARTX, RX, CTS> { 682 | type Error = Void; 683 | 684 | fn read(&mut self) -> nb::Result { 685 | // We're only doing RX operations here so this is safe. 686 | let p = unsafe { &*$UARTX::ptr() }; 687 | if p.fr.read().rxfe().bit() { 688 | return Err(nb::Error::WouldBlock); 689 | } 690 | Ok(p.dr.read().data().bits()) 691 | } 692 | } 693 | 694 | impl serial::Write for Serial<$UARTX, TX, RX, RTS, CTS> { 695 | type Error = Void; 696 | 697 | fn flush(&mut self) -> nb::Result<(), Void> { 698 | if self.uart.fr.read().txff().bit() { 699 | return Err(nb::Error::WouldBlock); 700 | } 701 | Ok(()) 702 | } 703 | 704 | fn write(&mut self, byte: u8) -> nb::Result<(), Void> { 705 | if self.uart.fr.read().txff().bit() { 706 | return Err(nb::Error::WouldBlock); 707 | } 708 | self.uart.dr.write(|w| unsafe { w.data().bits(byte) }); 709 | Ok(()) 710 | } 711 | } 712 | 713 | impl serial::Write for Tx<$UARTX, TX, RTS> { 714 | type Error = Void; 715 | 716 | fn flush(&mut self) -> nb::Result<(), Void> { 717 | if self.uart.fr.read().txff().bit() { 718 | return Err(nb::Error::WouldBlock); 719 | } 720 | Ok(()) 721 | } 722 | 723 | fn write(&mut self, byte: u8) -> nb::Result<(), Void> { 724 | if self.uart.fr.read().txff().bit() { 725 | return Err(nb::Error::WouldBlock); 726 | } 727 | self.uart.dr.write(|w| unsafe { w.data().bits(byte) }); 728 | Ok(()) 729 | } 730 | } 731 | 732 | /// Allows the Uart to be passed to 'write!()' and friends. 733 | impl fmt::Write for Serial<$UARTX, TX, RX, RTS, CTS> { 734 | fn write_str(&mut self, s: &str) -> fmt::Result { 735 | match self.nl_mode { 736 | NewlineMode::Binary => self.write_all(s), 737 | NewlineMode::SwapLFtoCRLF => { 738 | for byte in s.bytes() { 739 | if byte == 0x0A { 740 | // Prefix every \n with a \r 741 | block!(self.write(0x0D)).unwrap(); // E = Void 742 | } 743 | block!(self.write(byte)).unwrap(); // E = Void 744 | } 745 | } 746 | } 747 | Ok(()) 748 | } 749 | } 750 | 751 | /// Allows the Tx to be passed to 'write!()' and friends. 752 | impl fmt::Write for Tx<$UARTX, TX, RTS> { 753 | fn write_str(&mut self, s: &str) -> fmt::Result { 754 | match self.nl_mode { 755 | NewlineMode::Binary => self.write_all(s), 756 | NewlineMode::SwapLFtoCRLF => { 757 | for byte in s.bytes() { 758 | if byte == 0x0A { 759 | // Prefix every \n with a \r 760 | block!(self.write(0x0D)).unwrap(); // E = Void 761 | } 762 | block!(self.write(byte)).unwrap(); // E = Void 763 | } 764 | } 765 | } 766 | Ok(()) 767 | } 768 | } 769 | 770 | )+ 771 | } 772 | } 773 | 774 | ///! An internal macro to help define all the different pin typestates 775 | #[macro_export] 776 | macro_rules! uart_pin_macro { 777 | ($UARTn:ident, 778 | cts: [$(($($ctsgpio: ident)::*, $ctsaf: ident)),*], 779 | // dcd: [() $(, ($($dcdgpio: ident)::*, $dcdaf: ident))*], 780 | // dsr: [() $(, ($($dsrgpio: ident)::*, $dsraf: ident))*], 781 | // dtr: [() $(, ($($dtrgpio: ident)::*, $dtraf: ident))*], 782 | // ri: [() $(, ($($rigpio: ident)::*, $riaf: ident))*], 783 | rts: [$(($($rtsgpio: ident)::*, $rtsaf: ident)),*], 784 | rx: [$(($($rxgpio: ident)::*, $rxaf: ident)),*], 785 | tx: [$(($($txgpio: ident)::*, $txaf: ident)),*], 786 | ) => { 787 | $( 788 | impl CtsPin<$UARTn> for $($ctsgpio)::*> 789 | where 790 | T: OutputMode, 791 | { 792 | fn enable(&mut self, uart: &mut $UARTn) { 793 | uart.ctl.modify(|_, w| w.ctsen().set_bit()); 794 | } 795 | } 796 | )* 797 | 798 | $( 799 | impl RtsPin<$UARTn> for $($rtsgpio)::*> 800 | where 801 | T: OutputMode, 802 | { 803 | fn enable(&mut self, uart: &mut $UARTn) { 804 | uart.ctl.modify(|_, w| w.rtsen().set_bit()); 805 | } 806 | } 807 | )* 808 | 809 | $( 810 | impl RxPin<$UARTn> for $($rxgpio)::*> 811 | where 812 | T: OutputMode, 813 | {} 814 | )* 815 | 816 | $( 817 | impl TxPin<$UARTn> for $($txgpio)::*> 818 | where 819 | T: OutputMode, 820 | {} 821 | )* 822 | } 823 | } 824 | -------------------------------------------------------------------------------- /tm4c-hal/src/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial code that is generic to both the TM4C123 and TM4C129, such as the pin traits. 2 | 3 | // The macro is required for the "sealed trait" pattern to work: 4 | // the traits and the gpios have to be defined in the same crate 5 | 6 | ///! An internal macro to generate the UART traits 7 | #[macro_export] 8 | macro_rules! uart_traits_macro { 9 | () => { 10 | /// TX pin 11 | pub trait TxPin: crate::Sealed {} 12 | 13 | /// RX pin 14 | pub trait RxPin: crate::Sealed {} 15 | 16 | /// CTS pin 17 | pub trait CtsPin: crate::Sealed { 18 | /// Enables the CTS functionality if a valid pin is given (not `()`). 19 | fn enable(&mut self, _uart: &mut UART); 20 | } 21 | 22 | /// DCD pin 23 | pub trait DcdPin: crate::Sealed { 24 | /// Enables the DCD functionality if a valid pin is given (not `()`). 25 | fn enable(&mut self, _uart: &mut UART); 26 | } 27 | 28 | /// DSR pin 29 | pub trait DsrPin: crate::Sealed { 30 | /// Enables the DSR functionality if a valid pin is given (not `()`). 31 | fn enable(&mut self, _uart: &mut UART); 32 | } 33 | 34 | /// DTR pin 35 | pub trait DtrPin: crate::Sealed { 36 | /// Enables the DTR functionality if a valid pin is given (not `()`). 37 | fn enable(&mut self, _uart: &mut UART); 38 | } 39 | 40 | /// RI pin 41 | pub trait RiPin: crate::Sealed { 42 | /// Enables the RI functionality if a valid pin is given (not `()`). 43 | fn enable(&mut self, _uart: &mut UART); 44 | } 45 | 46 | /// RTS pin 47 | pub trait RtsPin: crate::Sealed { 48 | /// Enables the RTS functionality if a valid pin is given (not `()`). 49 | fn enable(&mut self, _uart: &mut UART); 50 | } 51 | 52 | impl TxPin for () {} 53 | 54 | impl RxPin for () {} 55 | 56 | impl CtsPin for () { 57 | fn enable(&mut self, _uart: &mut U) { 58 | // Do nothing 59 | } 60 | } 61 | impl DcdPin for () { 62 | fn enable(&mut self, _uart: &mut U) { 63 | // Do nothing 64 | } 65 | } 66 | impl DsrPin for () { 67 | fn enable(&mut self, _uart: &mut U) { 68 | // Do nothing 69 | } 70 | } 71 | impl DtrPin for () { 72 | fn enable(&mut self, _uart: &mut U) { 73 | // Do nothing 74 | } 75 | } 76 | impl RiPin for () { 77 | fn enable(&mut self, _uart: &mut U) { 78 | // Do nothing 79 | } 80 | } 81 | impl RtsPin for () { 82 | fn enable(&mut self, _uart: &mut U) { 83 | // Do nothing 84 | } 85 | } 86 | }; 87 | } 88 | 89 | /// writeln!() emits LF chars, so this is useful 90 | /// if you're writing text with your UART 91 | #[derive(PartialEq, Clone, Copy)] 92 | pub enum NewlineMode { 93 | /// Emit octets as received 94 | Binary, 95 | /// Emit an extra CR before every LF 96 | SwapLFtoCRLF, 97 | } 98 | -------------------------------------------------------------------------------- /tm4c-hal/src/sysctl.rs: -------------------------------------------------------------------------------- 1 | //! Code for the System Control module. 2 | 3 | use crate::time::Hertz; 4 | 5 | /// Frozen clock frequencies 6 | /// 7 | /// The existence of this value indicates that the clock configuration can no longer be changed 8 | #[derive(Clone, Copy)] 9 | pub struct Clocks { 10 | /// System oscillator clock speed 11 | pub osc: Hertz, 12 | /// System clock speed 13 | pub sysclk: Hertz, 14 | } 15 | 16 | #[derive(Copy, Clone)] 17 | /// Select in which mode the peripheral should be affected 18 | pub enum RunMode { 19 | /// Run mode 20 | Run, 21 | /// Sleep mode (i.e. WFI is being executed) 22 | Sleep, 23 | /// Deep-Sleep mode (i.e. WFI is being executed with SLEEP DEEP bit set) 24 | DeepSleep, 25 | } 26 | 27 | #[derive(Copy, Clone)] 28 | /// Select whether the peripheral should be on or off 29 | pub enum PowerState { 30 | /// Turn peripheral clocks/power off 31 | Off, 32 | /// Turn peripheral clocks/power on 33 | On, 34 | } 35 | 36 | impl Clocks { 37 | /// Returns the frequency of the oscillator. 38 | pub fn osc(self) -> Hertz { 39 | self.osc 40 | } 41 | 42 | /// Returns the system (core) frequency 43 | pub fn sysclk(self) -> Hertz { 44 | self.sysclk 45 | } 46 | } 47 | 48 | /// This module is all about identifying the physical chip we're running on. 49 | pub mod chip_id { 50 | 51 | /// Possible errors we can get back when parsing the ID registers. 52 | #[derive(Debug)] 53 | pub enum Error { 54 | /// Unknown value in DID0 55 | UnknownDid0Ver(u8), 56 | /// Unknown value in DID1 57 | UnknownDid1Ver(u8), 58 | } 59 | 60 | /// What sort of device is this? 61 | #[derive(Debug)] 62 | pub enum DeviceClass { 63 | /// It's a Stellaris LM4F or a TM4C123 (they have the same value) 64 | StellarisBlizzard, 65 | /// It's a "Tiva™ Snowflake-class microcontroller" 66 | Snowflake, 67 | /// I don't know what chip this is 68 | Unknown, 69 | } 70 | 71 | /// How many pins on this chip's package? 72 | #[derive(Debug)] 73 | pub enum PinCount { 74 | /// It's a 28 pin package 75 | _28, 76 | /// It's a 48 pin package 77 | _48, 78 | /// It's a 100 pin package 79 | _100, 80 | /// It's a 64 pin package 81 | _64, 82 | /// It's a 144 pin package 83 | _144, 84 | /// It's a 157 pin package 85 | _157, 86 | /// It's a 168 pin package (TM4C123 only) 87 | _168, 88 | /// I don't know what chip this is 89 | Unknown, 90 | } 91 | 92 | /// What temperature range does this chip support? 93 | #[derive(Debug)] 94 | pub enum TempRange { 95 | /// It's a Commercial temperature range part (0°C - +70°C) 96 | Commercial, 97 | /// It's a Industrial temperature range part (-40°C - +85°C) 98 | Industrial, 99 | /// It's a Extended temperature range part (-40°C - +105°C) 100 | Extended, 101 | /// It's either Extended or Industrial depending on the exact part 102 | /// number 103 | IndustrialOrExtended, 104 | /// I don't know what temperature range this is 105 | Unknown, 106 | } 107 | 108 | /// What package is this chip in? 109 | #[derive(Debug)] 110 | pub enum Package { 111 | /// It's a SOIC package 112 | Soic, 113 | /// It's a LQFP package 114 | Lqfp, 115 | /// It's a BGA package 116 | Bga, 117 | /// I don't know what package this is 118 | Unknown, 119 | } 120 | 121 | /// Is this an experimental chip or a production part? 122 | #[derive(Debug)] 123 | pub enum Qualification { 124 | /// It's a Engineering Sample chip 125 | EngineeringSample, 126 | /// It's a Pilot Production chip 127 | PilotProduction, 128 | /// It's a Fully Qualified chip 129 | FullyQualified, 130 | /// I don't know what qualification this is 131 | Unknown, 132 | } 133 | 134 | /// These values describe the part number 135 | #[derive(Debug)] 136 | pub enum PartNo { 137 | /// It's a TM4C123GH6PM 138 | Tm4c123gh6pm, 139 | /// It's a LM4F120H5QR 140 | Lm4f120h5qr, 141 | /// It's a TM4C1294NCPDT 142 | Tm4c1294ncpdt, 143 | /// It's a TM4C129ENCPDT 144 | Tm4c129encpdt, 145 | /// It's an unknown chip - please file a bug report 146 | Unknown(u8), 147 | } 148 | 149 | /// These values describe the physical LM4F/TM4C chip 150 | #[derive(Debug)] 151 | pub struct ChipId { 152 | /// The device class 153 | pub device_class: DeviceClass, 154 | /// The major revision 155 | pub major: u8, 156 | /// The minor revision 157 | pub minor: u8, 158 | /// The chip's pin count 159 | pub pin_count: PinCount, 160 | /// The chip's temperature range 161 | pub temp_range: TempRange, 162 | /// The chip's package 163 | pub package: Package, 164 | /// True if the chip is RoHS compliant 165 | pub rohs_compliant: bool, 166 | /// The chip's qualification 167 | pub qualification: Qualification, 168 | /// The chip's part number 169 | pub part_no: PartNo, 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tm4c-hal/src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | 3 | use cortex_m::peripheral::DWT; 4 | 5 | /// Bits per second 6 | #[derive(Clone, Copy)] 7 | pub struct Bps(pub u32); 8 | 9 | /// Hertz 10 | #[derive(Clone, Copy)] 11 | pub struct Hertz(pub u32); 12 | 13 | /// KiloHertz 14 | #[derive(Clone, Copy)] 15 | pub struct KiloHertz(pub u32); 16 | 17 | /// MegaHertz 18 | #[derive(Clone, Copy)] 19 | pub struct MegaHertz(pub u32); 20 | 21 | /// Extension trait that adds convenience methods to the `u32` type 22 | pub trait U32Ext { 23 | /// Wrap in `Bps` 24 | fn bps(self) -> Bps; 25 | 26 | /// Wrap in `Hertz` 27 | fn hz(self) -> Hertz; 28 | 29 | /// Wrap in `KiloHertz` 30 | fn khz(self) -> KiloHertz; 31 | 32 | /// Wrap in `MegaHertz` 33 | fn mhz(self) -> MegaHertz; 34 | } 35 | 36 | impl U32Ext for u32 { 37 | fn bps(self) -> Bps { 38 | Bps(self) 39 | } 40 | 41 | fn hz(self) -> Hertz { 42 | Hertz(self) 43 | } 44 | 45 | fn khz(self) -> KiloHertz { 46 | KiloHertz(self) 47 | } 48 | 49 | fn mhz(self) -> MegaHertz { 50 | MegaHertz(self) 51 | } 52 | } 53 | 54 | impl Into for KiloHertz { 55 | fn into(self) -> Hertz { 56 | Hertz(self.0 * 1_000) 57 | } 58 | } 59 | 60 | impl Into for MegaHertz { 61 | fn into(self) -> Hertz { 62 | Hertz(self.0 * 1_000_000) 63 | } 64 | } 65 | 66 | impl Into for MegaHertz { 67 | fn into(self) -> KiloHertz { 68 | KiloHertz(self.0 * 1_000) 69 | } 70 | } 71 | 72 | /// A monotonic nondecreasing timer 73 | #[derive(Clone, Copy)] 74 | pub struct MonoTimer { 75 | frequency: Hertz, 76 | } 77 | 78 | impl MonoTimer { 79 | /// Returns the frequency at which the monotonic timer is operating at 80 | pub fn frequency(self) -> Hertz { 81 | self.frequency 82 | } 83 | 84 | /// Returns an `Instant` corresponding to "now" 85 | pub fn now(self) -> Instant { 86 | Instant { 87 | now: DWT::get_cycle_count(), 88 | } 89 | } 90 | } 91 | 92 | /// A measurement of a monotonically nondecreasing clock 93 | #[derive(Clone, Copy)] 94 | pub struct Instant { 95 | now: u32, 96 | } 97 | 98 | impl Instant { 99 | /// Ticks elapsed since the `Instant` was created 100 | pub fn elapsed(self) -> u32 { 101 | DWT::get_cycle_count().wrapping_sub(self.now) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tm4c123x-hal/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tlink.x", 4 | ] 5 | 6 | [build] 7 | target = "thumbv7em-none-eabihf" 8 | -------------------------------------------------------------------------------- /tm4c123x-hal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tm4c123x-hal" 3 | version = "0.10.3" 4 | authors = [ 5 | "Jorge Aparicio ", 6 | "Jonathan 'theJPster' Pallant ", 7 | "Marc Poulhiès ", 8 | "David Wood ", 9 | ] 10 | description = "HAL for the TM4C123x family of microcontrollers" 11 | keywords = ["arm", "cortex-m", "tm4c", "lm4f120", "hal"] 12 | categories = ["embedded", "hardware-support", "no-std"] 13 | license = "MIT OR Apache-2.0" 14 | repository = "https://github.com/rust-embedded-community/tm4c-hal/tm4c123x-hal" 15 | edition = "2018" 16 | 17 | [dependencies.cast] 18 | version = "0.2.2" 19 | default-features = false 20 | 21 | [dependencies.cortex-m] 22 | version = "0.7" 23 | 24 | [dependencies.embedded-hal] 25 | version = "0.2.2" 26 | features = ["unproven"] 27 | 28 | [dependencies.nb] 29 | version = "1" 30 | 31 | [dependencies.tm4c123x] 32 | version = "0.9.1" 33 | 34 | [dependencies.void] 35 | version = "1.0" 36 | default-features = false 37 | 38 | [dependencies.tm4c-hal] 39 | path = "../tm4c-hal" 40 | version = "0.4.1" 41 | 42 | [features] 43 | rt = ["tm4c123x/rt"] 44 | -------------------------------------------------------------------------------- /tm4c123x-hal/README.md: -------------------------------------------------------------------------------- 1 | # `tm4c123x-hal` 2 | 3 | > HAL for the TM4C123x (and compatible LM4F120x) family of microcontrollers 4 | 5 | [`embedded-hal`]: https://crates.io/crates/embedded-hal 6 | 7 | ## [Documentation](https://docs.rs/tm4c123x-hal) 8 | 9 | ## Changelog 10 | 11 | ### Unreleased Changes ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/master/tm4c123x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c123x-hal-0.10.3...master)) 12 | 13 | * Use sealed traits for `*Pin` marker traits 14 | * Do not reexport `tm4c-hal` macros 15 | * Updated the dependencies for the supporting crate `tm4c123x` to 16 | `0.9.1` which supports newer version of `cortex-m`. This version can be used with newer 17 | versions of RTIC and has been tested in hardware (Launchpad and custom PCB) 18 | using RTIC `1.1.4`. Testing included SPI, ADC, Timers, EEPROM, GPIO, UART, 19 | and multiple interrupts (UART, GPIO, TIMERS, ADC). 20 | 21 | ### v0.10.2 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c123x-hal-0.10.2/tm4c123x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c123x-hal-0.10.2...tm4c123x-hal-0.10.1)) 22 | 23 | * Updated to tm4c-hal 0.4.1 24 | 25 | ### v0.10.1 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c123x-hal-0.10.1/tm4c123x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c123x-hal-0.10.1...tm4c123x-hal-0.10.0)) 26 | 27 | * Updated to tm4c-hal 0.4.0 28 | 29 | ### v0.10.0 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c123x-hal-0.10.0/tm4c123x-hal)) 30 | 31 | * Changelog starts here 32 | 33 | ## License 34 | 35 | Licensed under either of 36 | 37 | - Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or 38 | http://www.apache.org/licenses/LICENSE-2.0) 39 | - MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) 40 | 41 | at your option. 42 | 43 | ### Contribution 44 | 45 | Unless you explicitly state otherwise, any contribution intentionally submitted 46 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 47 | dual licensed as above, without any additional terms or conditions. 48 | -------------------------------------------------------------------------------- /tm4c123x-hal/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 | -------------------------------------------------------------------------------- /tm4c123x-hal/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 4 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 5 | } 6 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/eeprom.rs: -------------------------------------------------------------------------------- 1 | //! Code for the EEProm module. 2 | 3 | use core::convert::TryInto; 4 | 5 | use crate::sysctl::{self}; 6 | use cortex_m::asm::delay; 7 | use tm4c123x::EEPROM; 8 | pub use tm4c_hal::eeprom::{Blocks, Busy, EepromAddress, EepromError, Erase, Read, Write}; 9 | 10 | // Number of EEPROM block on the TM4C123 11 | const EEPROM_BLOCK_SIZE: usize = 16; 12 | 13 | // Number of EEPROM blocks on the TM4C123 14 | const EEPROM_NUM_BLOCKS: usize = 32; 15 | 16 | // Total number of bytes in the EEPROM on the TM4C123 17 | const EEPROM_END_ADDRESS_BYTES: usize = 2048; 18 | 19 | // Total number of words in the EEPROM on the TM4C123 20 | const EEPROM_END_ADDRESS_WORDS: usize = 512; 21 | 22 | // Size of the EEPROM word in bytes 23 | const BYTES_PER_WORD: usize = 4; 24 | 25 | /// Eeprom struct 26 | pub struct Eeprom { 27 | /// Eeprom registers 28 | eeprom: EEPROM, 29 | } 30 | 31 | impl Eeprom { 32 | /// Configures a new EEPROM struct using the datasheet section 8.2.4.2. Note: 33 | /// this function may panic if PRETRY and ERETRY in the EESUPP register 34 | /// are set during the initialization. Section 8.2.4.2 explains further: 35 | /// 36 | /// If the PRETRY or ERETRY bits are set in the EESUPP register, the EEPROM 37 | /// was unable to recover its state. If power is stable when this occurs, 38 | /// this indicates a fatal error and is likely an indication that the EEPROM 39 | /// memory has exceeded its specified lifetime write/erase specification. 40 | /// If the supply voltage is unstable when this return code is observed, 41 | /// retrying the operation once the voltage is stabilized may clear the 42 | /// error. 43 | pub fn new(eeprom: EEPROM, pc: &sysctl::PowerControl) -> Self { 44 | let final_eeprom = Eeprom { eeprom }; 45 | 46 | // See Section 8.2.4.2 EEPROM Initialization and Configuration 47 | // in the datasheet: 48 | 49 | // 0. Power on the EEPROM peripheral 50 | sysctl::control_power( 51 | pc, 52 | sysctl::Domain::Eeprom, 53 | tm4c_hal::sysctl::RunMode::Run, 54 | tm4c_hal::sysctl::PowerState::On, 55 | ); 56 | 57 | // 1. The datasheet calls for at least a 6 cycle delay before polling 58 | // the working register. Need to make sure the loop isn't optimized 59 | // out. 60 | delay(20); 61 | 62 | // 2. Poll busy 63 | final_eeprom.wait(); 64 | 65 | // 3. Read PRETRY and ERETRY 66 | // Note: If either bit is set, the data sheet indicates this is a pretty severe 67 | // error with the EEPROM and the EEPROM shouldn't be used, which is why 68 | // a panic!() is chosen over an error. There could be issues with the 69 | // chip, core voltage, or EEPROM; regardless, it probably should be 70 | // investigated further. 71 | // See section 8.2.4.2 72 | if final_eeprom.eeprom.eesupp.read().eretry().bit_is_set() { 73 | panic!("Eeprom ERETRY bit set, please investigate or stop using the EEPROM peripheral"); 74 | } 75 | 76 | if final_eeprom.eeprom.eesupp.read().pretry().bit_is_set() { 77 | panic!("Eeprom PRETRY bit set, please investigate or stop using the EEPROM peripheral"); 78 | } 79 | 80 | // 4. Software reset 81 | sysctl::reset(pc, sysctl::Domain::Eeprom); 82 | 83 | // 5. Another delay 84 | delay(20); 85 | 86 | // 6. Poll busy 87 | final_eeprom.wait(); 88 | 89 | // 7. Recheck PRETRY and ERETRY 90 | // Note: If either bit is set, the data sheet indicates this is a pretty severe 91 | // error with the EEPROM and the EEPROM shouldn't be used, which is why 92 | // a panic!() is chosen over an error. There could be issues with the 93 | // chip, core voltage, or EEPROM; regardless, it probably should be 94 | // investigated further. 95 | // See section 8.2.4.2 96 | if final_eeprom.eeprom.eesupp.read().eretry().bit_is_set() { 97 | panic!("Eeprom ERETRY bit set, please investigate or stop using the EEPROM peripheral"); 98 | } 99 | 100 | if final_eeprom.eeprom.eesupp.read().pretry().bit_is_set() { 101 | panic!("Eeprom PRETRY bit set, please investigate or stop using the EEPROM peripheral"); 102 | } 103 | 104 | // 8. All done 105 | final_eeprom 106 | } 107 | 108 | /// Set the block register 109 | fn set_block(&self, block: usize) -> Result<(), EepromError> { 110 | if self.is_busy() { 111 | return Err(EepromError::Busy); 112 | } 113 | 114 | if block < EEPROM_NUM_BLOCKS { 115 | unsafe { 116 | self.eeprom.eeblock.write(|w| w.bits(block as u32)); 117 | } 118 | 119 | // Changing blocks requires a small delay, see Section 8.2.4.1 Timing Considerations 120 | delay(4); 121 | 122 | self.wait(); 123 | 124 | Ok(()) 125 | } else { 126 | Err(EepromError::BlockOutOfBounds) 127 | } 128 | } 129 | 130 | /// Set the offset register 131 | fn set_offset(&self, offset: usize) -> Result<(), EepromError> { 132 | if self.is_busy() { 133 | return Err(EepromError::Busy); 134 | } 135 | 136 | if offset < EEPROM_BLOCK_SIZE { 137 | unsafe { 138 | self.eeprom.eeoffset.write(|w| w.bits(offset as u32)); 139 | } 140 | 141 | self.wait(); 142 | 143 | Ok(()) 144 | } else { 145 | Err(EepromError::OffsetOutOfBounds) 146 | } 147 | } 148 | 149 | /// Set the block and offset registers 150 | fn set_block_and_offset(&self, address: &EepromAddress) -> Result<(), EepromError> { 151 | self.wait(); 152 | self.set_block(address.block())?; 153 | self.set_offset(address.offset())?; 154 | Ok(()) 155 | } 156 | 157 | /// Checks if read / writing a certain number of bytes from an address is 158 | /// valid. Returns true if EEPROM access is valid, false if there 159 | /// are any issues (overflow or invalid address). 160 | fn is_access_valid(&self, address: &EepromAddress, length_bytes: usize) -> bool { 161 | // Check if the initial address is valid, then check byte length 162 | match self.address_to_word_index(&address) { 163 | Ok(start_word_address) => { 164 | return start_word_address * BYTES_PER_WORD + length_bytes 165 | < EEPROM_END_ADDRESS_BYTES; 166 | } 167 | Err(_) => { 168 | return false; 169 | } 170 | } 171 | } 172 | 173 | /// Increments the block and offset by 1 word. Will wrap both the offset and 174 | /// block to 0 if an increment would cause either to exceed their bounds. 175 | /// 176 | /// For example: 177 | /// * Block 0, Offset, 1 -> Block 0, Offset 2 178 | /// * Block 0, Offset, 15 -> Block 1, Offset 0 179 | /// * Block 31, Offset, 15 -> Block 0, Offset 0 180 | fn increment_offset( 181 | &mut self, 182 | starting_address: &mut EepromAddress, 183 | ) -> Result<(), EepromError> { 184 | starting_address.increment(EEPROM_BLOCK_SIZE, EEPROM_NUM_BLOCKS); 185 | self.set_block_and_offset(&starting_address)?; 186 | Ok(()) 187 | } 188 | } 189 | 190 | impl Busy for Eeprom { 191 | fn is_busy(&self) -> bool { 192 | self.eeprom.eedone.read().working().bit_is_set() 193 | } 194 | 195 | fn wait(&self) { 196 | while self.is_busy() == true {} 197 | } 198 | } 199 | 200 | impl Blocks for Eeprom { 201 | fn block_size(&self) -> Result { 202 | Ok(EEPROM_BLOCK_SIZE) 203 | } 204 | 205 | fn word_index_to_address(&self, word_address: usize) -> Result { 206 | if word_address > EEPROM_END_ADDRESS_WORDS { 207 | return Err(EepromError::AddressOutOfBounds); 208 | } else { 209 | let block = word_address / EEPROM_BLOCK_SIZE; 210 | let offset = word_address - (block * EEPROM_BLOCK_SIZE); 211 | Ok(EepromAddress::new(block, offset)) 212 | } 213 | } 214 | 215 | fn address_to_word_index(&self, block: &EepromAddress) -> Result { 216 | if block.block() > EEPROM_NUM_BLOCKS || block.offset() > EEPROM_BLOCK_SIZE { 217 | return Err(EepromError::BlockOutOfBounds); 218 | } else { 219 | return Ok(block.block() * EEPROM_BLOCK_SIZE + block.offset()); 220 | } 221 | } 222 | } 223 | 224 | impl Write for Eeprom { 225 | fn write(&mut self, address: &EepromAddress, data: &[u8]) -> Result<(), EepromError> { 226 | if self.is_busy() { 227 | return Err(EepromError::Busy); 228 | } 229 | 230 | // Check if the address is valid and if the data will fit 231 | if !self.is_access_valid(address, data.len()) { 232 | return Err(EepromError::WriteWouldOverflow); 233 | } 234 | 235 | self.set_block_and_offset(address)?; 236 | 237 | let chunk_iter = data.chunks_exact(4); 238 | let leftover_bytes = chunk_iter.remainder(); 239 | let mut address_copy = *address; 240 | 241 | for chunk in chunk_iter { 242 | let tmp = u32::from_le_bytes(chunk.try_into().unwrap()); 243 | 244 | self.wait(); 245 | 246 | unsafe { 247 | self.eeprom.eerdwr.write(|w| w.bits(tmp)); 248 | } 249 | 250 | self.increment_offset(&mut address_copy)?; 251 | } 252 | 253 | // Buffer the leftover bytes, if any, and write 254 | if leftover_bytes.len() != 0 { 255 | let mut buffer = [0 as u8; 4]; 256 | for (i, byte) in leftover_bytes.iter().enumerate() { 257 | buffer[i] = *byte; 258 | } 259 | 260 | self.wait(); 261 | 262 | unsafe { 263 | self.eeprom 264 | .eerdwr 265 | .write(|w| w.bits(u32::from_le_bytes(buffer))); 266 | } 267 | } 268 | 269 | self.wait(); 270 | 271 | Ok(()) 272 | } 273 | } 274 | 275 | impl Read for Eeprom { 276 | fn read( 277 | &mut self, 278 | address: &EepromAddress, 279 | bytes_to_read: usize, 280 | buffer: &mut [u8], 281 | ) -> Result<(), EepromError> { 282 | if self.is_busy() { 283 | return Err(EepromError::Busy); 284 | } 285 | 286 | if bytes_to_read > buffer.len() { 287 | return Err(EepromError::ReadBufferTooSmall); 288 | } 289 | 290 | if !self.is_access_valid(&address, bytes_to_read) { 291 | return Err(EepromError::ReadWouldOverflow); 292 | } 293 | 294 | let num_words = bytes_to_read / BYTES_PER_WORD; 295 | let leftover_bytes = bytes_to_read % BYTES_PER_WORD; 296 | let mut address_copy = *address; 297 | 298 | self.set_block_and_offset(&address)?; 299 | 300 | let mut byte_offset = 0; 301 | 302 | for _i in 0..num_words { 303 | self.wait(); 304 | 305 | let word_as_bytes = self.eeprom.eerdwr.read().bits().to_le_bytes(); 306 | 307 | self.increment_offset(&mut address_copy)?; 308 | 309 | for byte in word_as_bytes { 310 | buffer[byte_offset] = byte; 311 | byte_offset += 1; 312 | } 313 | } 314 | 315 | if leftover_bytes != 0 { 316 | self.wait(); 317 | 318 | let word_as_bytes = self.eeprom.eerdwr.read().bits().to_le_bytes(); 319 | 320 | self.increment_offset(&mut address_copy)?; 321 | 322 | for index in 0..leftover_bytes { 323 | buffer[byte_offset] = word_as_bytes[index]; 324 | byte_offset += 1; 325 | } 326 | } 327 | 328 | self.wait(); 329 | 330 | Ok(()) 331 | } 332 | } 333 | 334 | impl Erase for Eeprom { 335 | fn erase(&mut self, address: &EepromAddress, length_bytes: usize) -> Result<(), EepromError> { 336 | if self.is_busy() { 337 | return Err(EepromError::Busy); 338 | } 339 | 340 | if !self.is_access_valid(address, length_bytes) { 341 | return Err(EepromError::WriteWouldOverflow); 342 | } 343 | 344 | let num_words = length_bytes / BYTES_PER_WORD; 345 | let leftover_bytes = length_bytes % BYTES_PER_WORD; 346 | let mut address_copy = *address; 347 | 348 | self.set_block_and_offset(&address)?; 349 | 350 | let zero = 0 as u32; 351 | for _i in 0..num_words { 352 | self.wait(); 353 | 354 | unsafe { 355 | self.eeprom.eerdwr.write(|w| w.bits(zero)); 356 | } 357 | 358 | self.increment_offset(&mut address_copy)?; 359 | } 360 | 361 | // Special case here, need to read-modify-write 362 | if leftover_bytes != 0 { 363 | self.wait(); 364 | 365 | let mut word = self.eeprom.eerdwr.read().bits().to_le_bytes(); 366 | 367 | for i in 0..leftover_bytes { 368 | word[i] = 0; 369 | } 370 | 371 | unsafe { 372 | self.eeprom 373 | .eerdwr 374 | .write(|w| w.bits(u32::from_le_bytes(word))); 375 | } 376 | } 377 | 378 | self.wait(); 379 | 380 | Ok(()) 381 | } 382 | 383 | fn erase_block(&mut self, block: usize) -> Result<(), EepromError> { 384 | if self.is_busy() { 385 | return Err(EepromError::Busy); 386 | } 387 | 388 | self.set_block(block)?; 389 | 390 | let mut address = EepromAddress::new(block, 0); 391 | 392 | let zeros = [0 as u8; EEPROM_BLOCK_SIZE * BYTES_PER_WORD]; 393 | 394 | self.write(&mut address, &zeros)?; 395 | 396 | self.wait(); 397 | 398 | Ok(()) 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General Purpose Input / Output 2 | //! 3 | //! This module makes heavy use of types to try and ensure you can't have a 4 | //! pin in a mode you didn't expect. 5 | //! 6 | //! Most pins start in the `Tristate` state. You can call methods to convert 7 | //! them to inputs, outputs or put them into Alternate Function mode (e.g. to 8 | //! use with a UART). 9 | //! 10 | //! Some of the modes require extra information, and for that we use the so- 11 | //! called 'Turbo Fish` syntax, which looks like `method::`. 12 | //! 13 | //! If the operation is non-atomic, then you need to pass a mut-reference to 14 | //! the port's control structure. This ensures you can't change two pins in 15 | //! two threads at the same time. If the operation is fully atomic (using the 16 | //! chip's bit-banding feature) then this argument is not required. 17 | //! 18 | //! Here's an example: 19 | //! 20 | //! ``` 21 | //! # use tm4c123x_hal::*; 22 | //! # use tm4c123x_hal::sysctl::SysctlExt; 23 | //! # use tm4c123x_hal::gpio::GpioExt; 24 | //! # fn foo() { 25 | //! let p = Peripherals::take().unwrap(); 26 | //! let mut sc = p.SYSCTL.constrain(); 27 | //! let mut portb = p.GPIO_PORTB.split(&sc.power_control); 28 | //! let timer_output_pin = portb.pb0.into_af_push_pull::(&mut portb.control); 29 | //! let uart_tx_pin = portb.pb1.into_af_open_drain::(&mut portb.control); 30 | //! let blue_led = portb.pb2.into_push_pull_output(); 31 | //! let button = portb.pb3.into_pull_up_input(); 32 | //! # } 33 | //! ``` 34 | 35 | pub use tm4c_hal::gpio::*; 36 | 37 | use crate::{ 38 | bb, 39 | hal::digital::{InputPin, OutputPin, StatefulOutputPin}, 40 | sysctl, 41 | }; 42 | use core::marker::PhantomData; 43 | use tm4c_hal::gpio_macro; 44 | 45 | /// Extension trait to split a GPIO peripheral in independent pins and registers 46 | pub trait GpioExt { 47 | /// The to split the GPIO into 48 | type Parts; 49 | 50 | /// Splits the GPIO block into independent pins and registers 51 | fn split(self, power_control: &sysctl::PowerControl) -> Self::Parts; 52 | } 53 | 54 | gpio_macro!(tm4c123x, GPIO_PORTA, gpioa, GpioA, PAx, [ 55 | PA0: (pa0, 0, Tristate), 56 | PA1: (pa1, 1, Tristate), 57 | PA2: (pa2, 2, Tristate), 58 | PA3: (pa3, 3, Tristate), 59 | PA4: (pa4, 4, Tristate), 60 | PA5: (pa5, 5, Tristate), 61 | PA6: (pa6, 6, Tristate), 62 | PA7: (pa7, 7, Tristate), 63 | ]); 64 | 65 | gpio_macro!(tm4c123x, GPIO_PORTB, gpiob, GpioB, PBx, [ 66 | PB0: (pb0, 0, Tristate), 67 | PB1: (pb1, 1, Tristate), 68 | PB2: (pb2, 2, Tristate), 69 | PB3: (pb3, 3, Tristate), 70 | PB4: (pb4, 4, Tristate), 71 | PB5: (pb5, 5, Tristate), 72 | PB6: (pb6, 6, Tristate), 73 | PB7: (pb7, 7, Tristate), 74 | ]); 75 | 76 | gpio_macro!(tm4c123x, GPIO_PORTC, gpioc, GpioC, PCx, [ 77 | PC0: (pc0, 0, Locked), // JTAG/SWD pin 78 | PC1: (pc1, 1, Locked), // JTAG/SWD pin 79 | PC2: (pc2, 2, Locked), // JTAG/SWD pin 80 | PC3: (pc3, 3, Locked), // JTAG/SWD pin 81 | PC4: (pc4, 4, Tristate), 82 | PC5: (pc5, 5, Tristate), 83 | PC6: (pc6, 6, Tristate), 84 | PC7: (pc7, 7, Tristate), 85 | ]); 86 | 87 | gpio_macro!(tm4c123x, GPIO_PORTD, gpiod, GpioD, PDx, [ 88 | PD0: (pd0, 0, Tristate), 89 | PD1: (pd1, 1, Tristate), 90 | PD2: (pd2, 2, Tristate), 91 | PD3: (pd3, 3, Tristate), 92 | PD4: (pd4, 4, Tristate), 93 | PD5: (pd5, 5, Tristate), 94 | PD6: (pd6, 6, Tristate), 95 | PD7: (pd7, 7, Locked), // NMI pin 96 | ]); 97 | 98 | gpio_macro!(tm4c123x, GPIO_PORTE, gpioe, GpioE, PEx, [ 99 | PE0: (pe0, 0, Tristate), 100 | PE1: (pe1, 1, Tristate), 101 | PE2: (pe2, 2, Tristate), 102 | PE3: (pe3, 3, Tristate), 103 | PE4: (pe4, 4, Tristate), 104 | PE5: (pe5, 5, Tristate), 105 | PE6: (pe6, 6, Tristate), 106 | PE7: (pe7, 7, Tristate), 107 | ]); 108 | 109 | gpio_macro!(tm4c123x, GPIO_PORTF, gpiof, GpioF, PFx, [ 110 | PF0: (pf0, 0, Locked), // NMI pin 111 | PF1: (pf1, 1, Tristate), 112 | PF2: (pf2, 2, Tristate), 113 | PF3: (pf3, 3, Tristate), 114 | PF4: (pf4, 4, Tristate), 115 | PF5: (pf5, 5, Tristate), 116 | PF6: (pf6, 6, Tristate), 117 | PF7: (pf7, 7, Tristate), 118 | ]); 119 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/hib.rs: -------------------------------------------------------------------------------- 1 | //! A wrapper for the HIB (Hibernation) peripheral 2 | 3 | use crate::sysctl; 4 | 5 | /// Which source to use for the HIB clock 6 | pub enum Source { 7 | /// HIB clock is from an external oscillator 8 | ExternalOscillator, 9 | /// HIB clock is from the internal low-frequency oscillator 10 | LowFrequencyInternalOscillator, 11 | } 12 | 13 | /// A wrapper around the HIB (Hibernation) peripheral 14 | pub struct Hib { 15 | hib: tm4c123x::HIB, 16 | } 17 | 18 | impl Hib { 19 | /// Initialize the HIB peripheral, using a clock from `source` 20 | pub fn hib(hib: tm4c123x::HIB, source: Source, _pc: &sysctl::PowerControl) -> Self { 21 | hib.ctl.write(|w| { 22 | match source { 23 | Source::ExternalOscillator => w.oscbyp().set_bit(), 24 | Source::LowFrequencyInternalOscillator => w.oscbyp().clear_bit(), 25 | }; 26 | 27 | w.clk32en().set_bit(); 28 | 29 | w 30 | }); 31 | 32 | while hib.ctl.read().wrc().bit_is_clear() {} 33 | hib.ctl.write(|w| { 34 | match source { 35 | Source::ExternalOscillator => w.oscbyp().set_bit(), 36 | Source::LowFrequencyInternalOscillator => w.oscbyp().clear_bit(), 37 | }; 38 | 39 | w.oscdrv().set_bit(); 40 | w.clk32en().set_bit(); 41 | w.rtcen().set_bit(); 42 | 43 | w 44 | }); 45 | while hib.ctl.read().wrc().bit_is_clear() {} 46 | 47 | Hib { hib } 48 | } 49 | 50 | /// Get the current time, in units of (seconds, subseconds), where a 51 | /// subsecond is 1/32768 seconds 52 | pub fn get_time(&self) -> (u32, u16) { 53 | loop { 54 | let seconds = self.hib.rtcc.read().bits(); 55 | let subsec = self.hib.rtcss.read().rtcssc().bits(); 56 | 57 | if seconds == self.hib.rtcc.read().bits() { 58 | return (seconds, subsec); 59 | } 60 | } 61 | } 62 | 63 | /// Get the current time in milliseconds 64 | pub fn get_millis(&self) -> u64 { 65 | let (seconds, subsec) = self.get_time(); 66 | let seconds: u64 = seconds.into(); 67 | let subsec: u64 = subsec.into(); 68 | 69 | let millis: u64 = subsec * 1000 / 32_768; 70 | seconds * 1000 + millis 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Inter-Integrated Circuit (I2C) bus 2 | 3 | use crate::{ 4 | gpio::*, 5 | hal::blocking::i2c::{Read, Write, WriteRead}, 6 | sysctl::{self, Clocks}, 7 | time::Hertz, 8 | Sealed, 9 | }; 10 | 11 | use cortex_m::asm::delay; 12 | use tm4c123x::{I2C0, I2C1, I2C2, I2C3}; 13 | 14 | pub use tm4c_hal::i2c::Error; 15 | use tm4c_hal::{i2c_busy_wait, i2c_hal, i2c_pins}; 16 | 17 | /// I2C peripheral operating in master mode 18 | pub struct I2c { 19 | /// Underlying I2C peripheral 20 | pub i2c: I2C, 21 | /// Underlying GPIO pins used by peripheral 22 | pub pins: PINS, 23 | } 24 | 25 | /// SCL pin 26 | pub trait SclPin: Sealed {} 27 | 28 | /// SDA pin 29 | pub trait SdaPin: Sealed {} 30 | 31 | i2c_pins!(I2C0, scl: [(gpiob::PB2, AF3)], sda: [(gpiob::PB3, AF3)],); 32 | i2c_pins!(I2C1, scl: [(gpioa::PA6, AF3)], sda: [(gpioa::PA7, AF3)],); 33 | i2c_pins!(I2C2, scl: [(gpioe::PE4, AF3)], sda: [(gpioe::PE5, AF3)],); 34 | i2c_pins!(I2C3, scl: [(gpiod::PD0, AF3)], sda: [(gpiod::PD1, AF3)],); 35 | 36 | i2c_hal! { 37 | I2C0: (I2c0, i2c0), 38 | I2C1: (I2c1, i2c1), 39 | I2C2: (I2c2, i2c2), 40 | I2C3: (I2c3, i2c3), 41 | } 42 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! HAL for the tm4c123x family of microcontrollers 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the tm4c123x family of 4 | //! microcontrollers. 5 | //! 6 | //! [`embedded-hal`]: https://github.com/japaric/embedded-hal 7 | //! 8 | //! # Usage 9 | //! 10 | //! To build applications (binary crates) using this crate follow the [cortex-m-quickstart] 11 | //! instructions and add this crate as a dependency in step number 5 and make sure you enable the 12 | //! "rt" Cargo feature of this crate. 13 | //! 14 | //! [cortex-m-quickstart]: https://docs.rs/cortex-m-quickstart/~0.2.3 15 | //! 16 | //! # Examples 17 | //! 18 | //! Examples of *using* these abstractions like these can be found in the documentation of the [`f3`] crate. 19 | //! 20 | //! [`f3`]: https://docs.rs/f3/~0.5.1 21 | 22 | #![deny(missing_docs, warnings)] 23 | #![allow(deprecated)] 24 | #![no_std] 25 | 26 | pub use tm4c123x::{self, CorePeripherals, Peripherals}; 27 | pub use tm4c_hal::{bb, delay, time}; 28 | 29 | // Enable use of interrupt macro 30 | #[cfg(feature = "rt")] 31 | pub use crate::tm4c123x::interrupt; 32 | 33 | use embedded_hal as hal; 34 | 35 | use sealed::Sealed; 36 | mod sealed { 37 | // To prevent implementation of `*Pin` traits on arbitrary types 38 | pub trait Sealed {} 39 | 40 | impl Sealed for () {} 41 | } 42 | 43 | pub mod eeprom; 44 | pub mod gpio; 45 | pub mod hib; 46 | pub mod i2c; 47 | pub mod prelude; 48 | pub mod pwm; 49 | pub mod serial; 50 | pub mod spi; 51 | pub mod sysctl; 52 | pub mod timer; 53 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Prelude 2 | 3 | #[rustfmt::skip] 4 | pub use crate::{ 5 | gpio::GpioExt as _, 6 | hal::prelude::*, 7 | sysctl::SysctlExt, 8 | time::U32Ext, 9 | }; 10 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/pwm.rs: -------------------------------------------------------------------------------- 1 | //! PWM abstractions 2 | 3 | use crate::gpio::{gpiob, gpioc, gpiof, AlternateFunction, PushPull, AF7}; 4 | 5 | /// a timer 6 | pub struct Timer { 7 | timer: T, 8 | } 9 | 10 | /// Implemented for any pin that can be the Even CCP Pin (i.e. associated with timer A) of a timer 11 | /// peripheral. 12 | pub trait EvenPin {} 13 | 14 | /// Implemented for any pin that can be the Odd CCP Pin (i.e. associated with timer B) of a timer 15 | /// peripheral. 16 | pub trait OddPin {} 17 | 18 | /// PWM output from the even half (i.e. timer A) of a timer peripheral 19 | pub struct EvenPWM { 20 | timer: T, 21 | } 22 | 23 | /// PWM output from the odd half (i.e. timer B) of a timer peripheral 24 | pub struct OddPWM { 25 | timer: T, 26 | } 27 | 28 | macro_rules! into_one_half { 29 | ($Name:ident, $timer:path, $trait:path, $kind:ident, $mr:ident, $plo:ident, $mrsu:ident, 30 | $pwmie:ident, $cdir:ident, $ams:ident, $mr_module:ty) => { 31 | /// Create the PWM implementation for one half of a timer peripheral 32 | pub fn $Name(self, _pin: P) -> $kind<$timer> { 33 | self.timer.$mr.write(|w| { 34 | w.$plo().set_bit(); 35 | w.$mrsu().set_bit(); 36 | w.$pwmie().clear_bit(); 37 | w.$cdir().set_bit(); 38 | w.$ams().set_bit(); 39 | w.$mr().variant(<$mr_module>::PERIOD) 40 | }); 41 | $kind { timer: self.timer } 42 | } 43 | }; 44 | } 45 | 46 | macro_rules! impl_for_timer { 47 | ($Name:ident, $timer:path, $domain:expr, even: [$($($even_pins:ident)::+),+], 48 | odd: [$($($odd_pins:ident)::+),+]) => { 49 | impl_pwm!($timer); 50 | 51 | $( 52 | impl EvenPin> for $($even_pins)::+> {} 53 | )+ 54 | 55 | $( 56 | impl OddPin> for $($odd_pins)::+> {} 57 | )+ 58 | 59 | impl Timer<$timer> { 60 | /// Initialize timer for PWM usage 61 | pub fn $Name(power_control: &crate::sysctl::PowerControl, timer: $timer) -> Self { 62 | crate::sysctl::control_power( 63 | power_control, 64 | $domain, 65 | crate::sysctl::RunMode::Run, 66 | crate::sysctl::PowerState::On, 67 | ); 68 | crate::sysctl::reset(power_control, $domain); 69 | timer 70 | .cfg 71 | .modify(|_r, w| w.cfg().variant(tm4c123x::timer0::cfg::CFG_A::_16_BIT)); 72 | Timer { timer } 73 | } 74 | into_one_half!( 75 | into_even, 76 | $timer, 77 | EvenPin, 78 | EvenPWM, 79 | tamr, 80 | taplo, 81 | tamrsu, 82 | tapwmie, 83 | tacdir, 84 | taams, 85 | tm4c123x::timer0::tamr::TAMR_A 86 | ); 87 | into_one_half!( 88 | into_odd, 89 | $timer, 90 | OddPin, 91 | OddPWM, 92 | tbmr, 93 | tbplo, 94 | tbmrsu, 95 | tbpwmie, 96 | tbcdir, 97 | tbams, 98 | tm4c123x::timer0::tbmr::TBMR_A 99 | ); 100 | /// Create the PWM implementation for both halves of a timer peripheral 101 | pub fn into_both, O: OddPin>( 102 | self, 103 | even_pin: E, 104 | odd_pin: O, 105 | ) -> (EvenPWM<$timer>, OddPWM<$timer>) { 106 | // this is effectively cloning self, but the even and odd halves do not clobber the 107 | // same registers. I think this will be safe as long as tm4c123x::TIMERn is a 108 | // zero-sized type, or otherwise Timer remains safe to bitwise-copy. 109 | let mind: Self = unsafe { core::ptr::read(&self as *const _) }; 110 | let even = mind.into_even(even_pin); 111 | let odd = self.into_odd(odd_pin); 112 | (even, odd) 113 | } 114 | } 115 | }; 116 | } 117 | 118 | macro_rules! pwm_half { 119 | ($StructName:ident, $timer:path, $en_bit:expr, $ilr:ident, $matchr:ident) => { 120 | /// One half of a PWM timer 121 | impl embedded_hal::Pwm for $StructName<$timer> { 122 | type Channel = (); 123 | type Time = u32; // clock cycles, proper abstraction tbd 124 | type Duty = u32; // also clock cycles 125 | 126 | fn enable(&mut self, _: ()) { 127 | unsafe { crate::bb::change_bit(&self.timer.ctl, $en_bit, true) } 128 | } 129 | 130 | fn disable(&mut self, _: ()) { 131 | unsafe { crate::bb::change_bit(&self.timer.ctl, $en_bit, false) } 132 | } 133 | 134 | fn get_period(&self) -> Self::Time { 135 | self.timer.$ilr.read().bits() 136 | } 137 | 138 | fn set_period>(&mut self, period: P) { 139 | self.timer.$ilr.write(|w| unsafe { w.bits(period.into()) }); 140 | } 141 | 142 | fn get_duty(&self, _: ()) -> Self::Duty { 143 | let thresh = self.timer.$matchr.read().bits(); 144 | let period = self.get_period(); 145 | period - thresh 146 | } 147 | 148 | fn get_max_duty(&self) -> Self::Duty { 149 | self.get_period() 150 | } 151 | 152 | fn set_duty(&mut self, _: (), duty: Self::Duty) { 153 | self.timer 154 | .$matchr 155 | .write(|w| unsafe { w.bits(self.get_period() - duty) }); 156 | } 157 | } 158 | }; 159 | } 160 | 161 | macro_rules! impl_pwm { 162 | ($timer:path) => { 163 | pwm_half!(EvenPWM, $timer, 0, tailr, tamatchr); 164 | pwm_half!(OddPWM, $timer, 8, tbilr, tbmatchr); 165 | }; 166 | } 167 | 168 | impl_for_timer!( 169 | timer0, 170 | tm4c123x::TIMER0, 171 | crate::sysctl::Domain::Timer0, 172 | even: [gpiob::PB6, gpiof::PF0], 173 | odd: [gpiob::PB7, gpiof::PF1] 174 | ); 175 | 176 | impl_for_timer!( 177 | timer1, 178 | tm4c123x::TIMER1, 179 | crate::sysctl::Domain::Timer1, 180 | even: [gpiof::PF2, gpiob::PB4], 181 | odd: [gpiof::PF3, gpiob::PB5] 182 | ); 183 | 184 | impl_for_timer!( 185 | timer2, 186 | tm4c123x::TIMER2, 187 | crate::sysctl::Domain::Timer2, 188 | even: [gpiof::PF4, gpiob::PB0], 189 | odd: [gpiob::PB1] 190 | ); 191 | 192 | impl_for_timer!( 193 | timer3, 194 | tm4c123x::TIMER3, 195 | crate::sysctl::Domain::Timer3, 196 | even: [gpiob::PB2], 197 | odd: [gpiob::PB3] 198 | ); 199 | 200 | impl_for_timer!( 201 | timer4, 202 | tm4c123x::TIMER4, 203 | crate::sysctl::Domain::Timer4, 204 | even: [gpioc::PC0], 205 | odd: [gpioc::PC1] 206 | ); 207 | 208 | impl_for_timer!( 209 | timer5, 210 | tm4c123x::TIMER5, 211 | crate::sysctl::Domain::Timer5, 212 | even: [gpioc::PC2], 213 | odd: [gpioc::PC3] 214 | ); 215 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial 2 | 3 | // uart_hal_macro can be called with too-many arguments 4 | #![allow(clippy::too_many_arguments)] 5 | 6 | pub use tm4c123x::{UART0, UART1, UART2, UART3, UART4, UART5, UART6, UART7}; 7 | pub use tm4c_hal::serial::NewlineMode; 8 | use tm4c_hal::{uart_hal_macro, uart_pin_macro}; 9 | 10 | #[rustfmt::skip] 11 | use crate::{ 12 | gpio::{ 13 | gpioa, gpiob, gpioc, gpiod, gpioe, gpiof, 14 | AlternateFunction, OutputMode, AF1, AF2, AF8, 15 | }, 16 | hal::{prelude::*, serial}, 17 | sysctl, 18 | sysctl::Clocks, 19 | time::Bps, 20 | }; 21 | use core::{fmt, marker::PhantomData}; 22 | use nb::{self, block}; 23 | use void::Void; 24 | 25 | /// Serial abstraction 26 | pub struct Serial { 27 | uart: UART, 28 | tx_pin: TX, 29 | rx_pin: RX, 30 | rts_pin: RTS, 31 | cts_pin: CTS, 32 | nl_mode: NewlineMode, 33 | } 34 | 35 | /// Serial receiver 36 | pub struct Rx { 37 | _uart: PhantomData, 38 | pin: RX, 39 | flow_pin: CTS, 40 | } 41 | 42 | /// Serial transmitter 43 | pub struct Tx { 44 | uart: UART, 45 | pin: TX, 46 | flow_pin: RTS, 47 | nl_mode: NewlineMode, 48 | } 49 | 50 | uart_pin_macro!(UART0, 51 | cts: [], 52 | rts: [], 53 | rx: [(gpioa::PA0, AF1)], 54 | tx: [(gpioa::PA1, AF1)], 55 | ); 56 | 57 | uart_pin_macro!(UART1, 58 | cts: [(gpioc::PC5, AF8), (gpiof::PF1, AF1)], 59 | rts: [(gpioc::PC4, AF8), (gpiof::PF0, AF1)], 60 | rx: [(gpiob::PB0, AF1), (gpioc::PC4, AF2)], 61 | tx: [(gpiob::PB1, AF1), (gpioc::PC5, AF2)], 62 | ); 63 | 64 | uart_pin_macro!(UART2, 65 | cts: [], 66 | rts: [], 67 | rx: [(gpiod::PD6, AF1)], 68 | tx: [(gpiod::PD7, AF1)], 69 | ); 70 | 71 | uart_pin_macro!(UART3, 72 | cts: [], 73 | rts: [], 74 | rx: [(gpioc::PC6, AF1)], 75 | tx: [(gpioc::PC7, AF1)], 76 | ); 77 | 78 | uart_pin_macro!(UART4, 79 | cts: [], 80 | rts: [], 81 | rx: [(gpioc::PC4, AF1)], 82 | tx: [(gpioc::PC5, AF1)], 83 | ); 84 | 85 | uart_pin_macro!(UART5, 86 | cts: [], 87 | rts: [], 88 | rx: [(gpioe::PE4, AF1)], 89 | tx: [(gpioe::PE5, AF1)], 90 | ); 91 | 92 | uart_pin_macro!(UART6, 93 | cts: [], 94 | rts: [], 95 | rx: [(gpiod::PD4, AF1)], 96 | tx: [(gpiod::PD5, AF1)], 97 | ); 98 | 99 | uart_pin_macro!(UART7, 100 | cts: [], 101 | rts: [], 102 | rx: [(gpioe::PE0, AF1)], 103 | tx: [(gpioe::PE1, AF1)], 104 | ); 105 | 106 | uart_hal_macro! { 107 | UART0: (Uart0, uart0), 108 | UART1: (Uart1, uart1), 109 | UART2: (Uart2, uart2), 110 | UART3: (Uart3, uart3), 111 | UART4: (Uart4, uart4), 112 | UART5: (Uart5, uart5), 113 | UART6: (Uart6, uart6), 114 | UART7: (Uart7, uart7), 115 | } 116 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/spi.rs: -------------------------------------------------------------------------------- 1 | //! Serial Peripheral Interface (SPI) bus 2 | 3 | pub use crate::hal::spi::{Mode, MODE_0, MODE_1, MODE_2, MODE_3}; 4 | 5 | use crate::{ 6 | gpio::{ 7 | gpioa::{PA2, PA4, PA5}, 8 | gpiob::{PB4, PB6, PB7}, 9 | gpiod::{PD0, PD2, PD3}, 10 | AlternateFunction, OutputMode, AF1, AF2, 11 | }, 12 | hal::spi::{FullDuplex, Phase, Polarity}, 13 | sysctl, 14 | sysctl::Clocks, 15 | time::Hertz, 16 | Sealed, 17 | }; 18 | 19 | use tm4c123x::{SSI0, SSI1, SSI2, SSI3}; 20 | 21 | /// SPI error 22 | #[derive(Debug)] 23 | pub enum Error { 24 | #[doc(hidden)] 25 | _Extensible, 26 | } 27 | 28 | /// SCK pin 29 | pub trait SckPin: Sealed {} 30 | 31 | /// MISO pin 32 | pub trait MisoPin: Sealed {} 33 | 34 | /// MOSI pin 35 | pub trait MosiPin: Sealed {} 36 | 37 | // SSI0 38 | impl SckPin for PA2> where T: OutputMode {} 39 | impl MisoPin for PA4> where T: OutputMode {} 40 | impl MosiPin for PA5> where T: OutputMode {} 41 | 42 | // SSI1 43 | impl SckPin for PD0> where T: OutputMode {} 44 | impl MisoPin for PD2> where T: OutputMode {} 45 | impl MosiPin for PD3> where T: OutputMode {} 46 | 47 | // SSI2 48 | impl SckPin for PB4> where T: OutputMode {} 49 | impl MisoPin for PB6> where T: OutputMode {} 50 | impl MosiPin for PB7> where T: OutputMode {} 51 | 52 | // SSI3 53 | impl SckPin for PD0> where T: OutputMode {} 54 | impl MisoPin for PD2> where T: OutputMode {} 55 | impl MosiPin for PD3> where T: OutputMode {} 56 | 57 | /// SPI peripheral operating in full duplex master mode 58 | pub struct Spi { 59 | spi: SPI, 60 | pins: PINS, 61 | } 62 | 63 | macro_rules! busy_wait { 64 | ($spi:expr, $flag:ident, $op:ident) => { 65 | loop { 66 | let sr = $spi.sr.read(); 67 | if sr.$flag().$op() { 68 | break; 69 | } 70 | } 71 | }; 72 | } 73 | 74 | macro_rules! hal { 75 | ($($SPIX:ident: ($powerDomain:ident, $spiX:ident),)+) => { 76 | $( 77 | impl Spi<$SPIX, (SCK, MISO, MOSI)> { 78 | /// Configures the SPI peripheral to operate in full duplex master mode 79 | pub fn $spiX( 80 | spi: $SPIX, 81 | pins: (SCK, MISO, MOSI), 82 | mode: Mode, 83 | freq: F, 84 | clocks: &Clocks, 85 | pc: &sysctl::PowerControl, 86 | ) -> Self 87 | where 88 | F: Into, 89 | SCK: SckPin<$SPIX>, 90 | MISO: MisoPin<$SPIX>, 91 | MOSI: MosiPin<$SPIX>, 92 | { 93 | // power up 94 | sysctl::control_power( 95 | pc, sysctl::Domain::$powerDomain, 96 | sysctl::RunMode::Run, sysctl::PowerState::On); 97 | sysctl::reset(pc, sysctl::Domain::$powerDomain); 98 | 99 | // write 0 (reset value) for master operation. 100 | spi.cr1.write(|w| w); 101 | 102 | // SSICC Clock setup 103 | // set to reset value (0 = use system clock) 104 | spi.cc.write(|w| w); 105 | 106 | // Use Moto/SPI & 8bits data size 107 | let scr: u8; 108 | let mut cpsr = 2u32; 109 | let target_bitrate : u32 = clocks.sysclk.0 / freq.into().0; 110 | 111 | // Find solution for 112 | // SSInClk = SysClk / (CPSDVSR * (1 + SCR)) 113 | // with: 114 | // CPSDVSR in [2,254] 115 | // SCR in [0,255] 116 | 117 | loop { 118 | let scr32 = (target_bitrate / cpsr) - 1; 119 | if scr32 < 255 { 120 | scr = scr32 as u8; 121 | break; 122 | } 123 | cpsr += 2; 124 | assert!(cpsr <= 254); 125 | } 126 | 127 | let cpsr = cpsr as u8; 128 | 129 | spi.cpsr.write(|w| unsafe { 130 | w.cpsdvsr().bits(cpsr) 131 | }); 132 | 133 | spi.cr0.modify(|_,w| unsafe { 134 | w.spo().bit(mode.polarity == Polarity::IdleHigh) 135 | .sph().bit(mode.phase == Phase::CaptureOnSecondTransition) 136 | .frf().moto() 137 | .dss()._8() 138 | .scr().bits(scr) 139 | }); 140 | 141 | // Enable peripheral 142 | spi.cr1.write(|w| w.sse().set_bit()); 143 | 144 | Spi { spi, pins } 145 | } 146 | 147 | /// Releases the SPI peripheral and associated pins 148 | pub fn free(self) -> ($SPIX, (SCK, MISO, MOSI)) { 149 | (self.spi, self.pins) 150 | } 151 | 152 | /// Change the clock frequency of the SPI device. 153 | pub fn reclock(&mut self, freq: F, clocks: &Clocks) where F: Into { 154 | // Disable peripheral 155 | self.spi.cr1.modify(|_, w| w.sse().clear_bit()); 156 | 157 | let scr: u8; 158 | let mut cpsr = 2u32; 159 | let target_bitrate : u32 = clocks.sysclk.0 / freq.into().0; 160 | 161 | // Find solution for 162 | // SSInClk = SysClk / (CPSDVSR * (1 + SCR)) 163 | // with: 164 | // CPSDVSR in [2,254] 165 | // SCR in [0,255] 166 | 167 | loop { 168 | let scr32 = (target_bitrate / cpsr) - 1; 169 | if scr32 < 255 { 170 | scr = scr32 as u8; 171 | break; 172 | } 173 | cpsr += 2; 174 | assert!(cpsr <= 254); 175 | } 176 | 177 | let cpsr = cpsr as u8; 178 | 179 | self.spi.cpsr.write(|w| unsafe { w.cpsdvsr().bits(cpsr) }); 180 | self.spi.cr0.modify(|_,w| unsafe { w.scr().bits(scr) }); 181 | 182 | // Enable peripheral again 183 | self.spi.cr1.modify(|_, w| w.sse().set_bit()); 184 | } 185 | } 186 | 187 | impl FullDuplex for Spi<$SPIX, PINS> { 188 | type Error = Error; 189 | 190 | fn read(&mut self) -> nb::Result { 191 | // Receive FIFO Not Empty 192 | if self.spi.sr.read().rne().bit_is_clear() { 193 | Err(nb::Error::WouldBlock) 194 | } else { 195 | let r = self.spi.dr.read().data().bits() as u8; 196 | Ok(r) 197 | } 198 | } 199 | 200 | fn send(&mut self, byte: u8) -> nb::Result<(), Error> { 201 | // Transmit FIFO Not Full 202 | if self.spi.sr.read().tnf().bit_is_clear() { 203 | Err(nb::Error::WouldBlock) 204 | } else { 205 | self.spi.dr.write(|w| unsafe { 206 | w.data().bits(byte.into()) 207 | }); 208 | busy_wait!(self.spi, bsy, bit_is_clear); 209 | Ok(()) 210 | } 211 | } 212 | } 213 | 214 | impl crate::hal::blocking::spi::transfer::Default for Spi<$SPIX, PINS> {} 215 | 216 | impl crate::hal::blocking::spi::write::Default for Spi<$SPIX, PINS> {} 217 | )+ 218 | } 219 | } 220 | 221 | hal! { 222 | SSI0: (Ssi0, spi0), 223 | SSI1: (Ssi1, spi1), 224 | SSI2: (Ssi2, spi2), 225 | SSI3: (Ssi3, spi3), 226 | } 227 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | 3 | pub use tm4c_hal::time::*; 4 | 5 | use crate::sysctl::Clocks; 6 | 7 | impl MonoTimer { 8 | /// Creates a new `Monotonic` timer 9 | pub fn new(mut dwt: DWT, clocks: Clocks) -> Self { 10 | dwt.enable_cycle_counter(); 11 | 12 | // now the CYCCNT counter can't be stopped or resetted 13 | drop(dwt); 14 | 15 | MonoTimer { 16 | frequency: clocks.sysclk(), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tm4c123x-hal/src/timer.rs: -------------------------------------------------------------------------------- 1 | //! Timers 2 | 3 | use crate::{ 4 | hal::timer::{CountDown, Periodic}, 5 | sysctl::{self, Clocks}, 6 | }; 7 | 8 | #[rustfmt::skip] 9 | use tm4c123x::{ 10 | TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, 11 | WTIMER0, WTIMER1, WTIMER2, WTIMER3, WTIMER4, WTIMER5, 12 | }; 13 | use tm4c_hal::time::Hertz; 14 | use void::Void; 15 | 16 | /// Hardware timers 17 | pub struct Timer { 18 | tim: TIM, 19 | clocks: Clocks, 20 | timeout: Hertz, 21 | } 22 | 23 | /// Interrupt events 24 | pub enum Event { 25 | /// Timer timed out / count down ended 26 | TimeOut, 27 | } 28 | 29 | macro_rules! hal { 30 | ($($TIM:ident: ($tim:ident, $powerDomain:ident),)+) => { 31 | $( 32 | impl Periodic for Timer<$TIM> {} 33 | 34 | impl CountDown for Timer<$TIM> { 35 | type Time = Hertz; 36 | 37 | #[allow(unused_unsafe)] 38 | fn start(&mut self, timeout: T) 39 | where 40 | T: Into, 41 | { 42 | // Disable timer 43 | self.tim.ctl.modify(|_, w| 44 | w.taen().clear_bit() 45 | .tben().clear_bit() 46 | ); 47 | self.timeout = timeout.into(); 48 | 49 | let frequency = self.timeout.0; 50 | let ticks = self.clocks.sysclk.0 / frequency; 51 | 52 | self.tim.tav.write(|w| unsafe { w.bits(ticks) }); 53 | self.tim.tailr.write(|w| unsafe { w.bits(ticks) }); 54 | 55 | // // start counter 56 | self.tim.ctl.modify(|_, w| 57 | w.taen().set_bit() 58 | ); 59 | } 60 | 61 | fn wait(&mut self) -> nb::Result<(), Void> { 62 | if self.tim.ris.read().tatoris().bit_is_clear () { 63 | Err(nb::Error::WouldBlock) 64 | } else { 65 | self.tim.icr.write(|w| w.tatocint().set_bit()); 66 | Ok(()) 67 | } 68 | } 69 | } 70 | 71 | impl Timer<$TIM> { 72 | // XXX(why not name this `new`?) bummer: constructors need to have different names 73 | // even if the `$TIM` are non overlapping (compare to the `free` function below 74 | // which just works) 75 | /// Configures a TIM peripheral as a periodic count down timer 76 | pub fn $tim(tim: $TIM, timeout: T, 77 | pc: &sysctl::PowerControl, 78 | clocks: &Clocks, 79 | ) -> Self 80 | where 81 | T: Into, 82 | { 83 | // power up 84 | sysctl::control_power( 85 | pc, sysctl::Domain::$powerDomain, 86 | sysctl::RunMode::Run, sysctl::PowerState::On); 87 | sysctl::reset(pc, sysctl::Domain::$powerDomain); 88 | 89 | // Stop Timers 90 | tim.ctl.write(|w| 91 | w.taen().clear_bit() 92 | .tben().clear_bit() 93 | .tastall().set_bit() 94 | ); 95 | 96 | // GPTMCFG = 0x0 (chained - 2x16 = 32bits) This 97 | // will not force 32bits wide timer, this will 98 | // really force the wider range to be used (32 for 99 | // 16/32bits timers, 64 for 32/64). 100 | tim.cfg.write(|w| w.cfg()._32_bit_timer()); 101 | 102 | tim.tamr.write(|w| w.tamr().period()); 103 | 104 | let mut timer = Timer { 105 | tim, 106 | clocks: *clocks, 107 | timeout: Hertz(0), 108 | }; 109 | timer.start(timeout); 110 | 111 | timer 112 | } 113 | 114 | /// Starts listening for an `event` 115 | pub fn listen(&mut self, event: Event) { 116 | match event { 117 | Event::TimeOut => { 118 | // Enable update event interrupt 119 | self.tim.imr.modify(|_,w| w.tatoim().set_bit()); 120 | } 121 | } 122 | } 123 | 124 | /// Stops listening for an `event` 125 | pub fn unlisten(&mut self, event: Event) { 126 | match event { 127 | Event::TimeOut => { 128 | // Enable update event interrupt 129 | self.tim.imr.modify(|_,w| w.tatoim().clear_bit()); 130 | } 131 | } 132 | } 133 | 134 | /// Releases the TIM peripheral 135 | pub fn free(self) -> $TIM { 136 | // pause counter 137 | self.tim.ctl.write(|w| 138 | w.taen().clear_bit() 139 | .tben().clear_bit()); 140 | self.tim 141 | } 142 | } 143 | )+ 144 | } 145 | } 146 | 147 | hal! { 148 | TIMER0: (timer0, Timer0), 149 | TIMER1: (timer1, Timer1), 150 | TIMER2: (timer2, Timer2), 151 | TIMER3: (timer3, Timer3), 152 | TIMER4: (timer4, Timer4), 153 | TIMER5: (timer5, Timer5), 154 | 155 | WTIMER0: (wtimer0, WideTimer0), 156 | WTIMER1: (wtimer1, WideTimer1), 157 | WTIMER2: (wtimer2, WideTimer2), 158 | WTIMER3: (wtimer3, WideTimer3), 159 | WTIMER4: (wtimer4, WideTimer4), 160 | WTIMER5: (wtimer5, WideTimer5), 161 | } 162 | -------------------------------------------------------------------------------- /tm4c129x-hal/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabihf] 2 | rustflags = [ 3 | "-C", "link-arg=-Tlink.x", 4 | ] 5 | 6 | [build] 7 | target = "thumbv7em-none-eabihf" 8 | -------------------------------------------------------------------------------- /tm4c129x-hal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tm4c129x-hal" 3 | version = "0.9.3" 4 | authors = [ 5 | "Jorge Aparicio ", 6 | "Jonathan 'theJPster' Pallant ", 7 | "Marc Poulhiès ", 8 | "David Wood ", 9 | ] 10 | description = "HAL for the TM4C129x family of microcontrollers" 11 | keywords = ["arm", "cortex-m", "tm4c", "hal"] 12 | categories = ["embedded", "hardware-support", "no-std"] 13 | license = "MIT OR Apache-2.0" 14 | repository = "https://github.com/rust-embedded-community/tm4c-hal/tm4c129x-hal" 15 | edition = "2018" 16 | 17 | [dependencies] 18 | cortex-m = "0.7" 19 | nb = "1" 20 | 21 | [dependencies.tm4c129x] 22 | version = "0.9.1" 23 | 24 | [dependencies.cast] 25 | version = "0.2" 26 | default-features = false 27 | 28 | [dependencies.embedded-hal] 29 | version = "0.2" 30 | features = ["unproven"] 31 | 32 | [dependencies.void] 33 | version = "1.0" 34 | default-features = false 35 | 36 | [dependencies.tm4c-hal] 37 | version = "0.4.1" 38 | path = "../tm4c-hal" 39 | 40 | [features] 41 | rt = ["tm4c129x/rt"] 42 | -------------------------------------------------------------------------------- /tm4c129x-hal/README.md: -------------------------------------------------------------------------------- 1 | # `tm4c129x-hal` 2 | 3 | > HAL for the TM4C129x family of microcontrollers 4 | 5 | [`embedded-hal`]: https://crates.io/crates/embedded-hal 6 | 7 | ## [Documentation](https://docs.rs/tm4c129x-hal) 8 | 9 | ## Changelog 10 | 11 | ### Unreleased Changes ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/master/tm4c129x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c129x-hal-0.9.3...master)) 12 | 13 | * Use sealed traits for `*Pin` marker traits 14 | * Do not reexport `tm4c-hal` macros 15 | * Updated the dependencies for the supporting crate `tm4c129x` to 16 | `0.9.1` which supports newer version of `cortex-m`. This _should_ allow for running 17 | newer version of RTIC / `cortex-m`, however, unlike the `tm4c123` this hasn't been 18 | tested. 19 | 20 | 21 | ### v0.9.2 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c129x-hal-0.9.2/tm4c129x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c129x-hal-0.9.2...tm4c129x-hal-0.9.1)) 22 | 23 | * Updated to tm4c-hal 0.4.1 24 | 25 | ### v0.9.1 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c129x-hal-0.9.1/tm4c129x-hal) [Diff](https://github.com/rust-embedded-community/tm4c-hal/compare/tm4c129x-hal-0.9.1...tm4c129x-hal-0.9.0)) 26 | 27 | * Updated to tm4c-hal 0.4.0 28 | 29 | ### v0.9.0 ([Source](https://github.com/rust-embedded-community/tm4c-hal/tree/tm4c129x-hal-0.9.0/tm4c129x-hal)) 30 | 31 | * Changelog starts here 32 | 33 | ## License 34 | 35 | Licensed under either of 36 | 37 | - Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or 38 | http://www.apache.org/licenses/LICENSE-2.0) 39 | - MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) 40 | 41 | at your option. 42 | 43 | ### Contribution 44 | 45 | Unless you explicitly state otherwise, any contribution intentionally submitted 46 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 47 | dual licensed as above, without any additional terms or conditions. 48 | -------------------------------------------------------------------------------- /tm4c129x-hal/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 | -------------------------------------------------------------------------------- /tm4c129x-hal/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00100000 4 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00040000 5 | } 6 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/gpio.rs: -------------------------------------------------------------------------------- 1 | //! General Purpose Input / Output 2 | //! 3 | //! This module makes heavy use of types to try and ensure you can't have a 4 | //! pin in a mode you didn't expect. 5 | //! 6 | //! Most pins start in the `Tristate` state. You can call methods to convert 7 | //! them to inputs, outputs or put them into Alternate Function mode (e.g. to 8 | //! use with a UART). 9 | //! 10 | //! Some of the modes require extra information, and for that we use the so- 11 | //! called 'Turbo Fish` syntax, which looks like `method::`. 12 | //! 13 | //! If the operation is non-atomic, then you need to pass a mut-reference to 14 | //! the port's control structure. This ensures you can't change two pins in 15 | //! two threads at the same time. If the operation is fully atomic (using the 16 | //! chip's bit-banding feature) then this argument is not required. 17 | //! 18 | //! Here's an example: 19 | //! 20 | //! ``` 21 | //! # use tm4c129x_hal::*; 22 | //! # use tm4c129x_hal::sysctl::SysctlExt; 23 | //! # use tm4c129x_hal::gpio::GpioExt; 24 | //! # fn foo() { 25 | //! let p = Peripherals::take().unwrap(); 26 | //! let mut sc = p.SYSCTL.constrain(); 27 | //! let mut portb = p.GPIO_PORTB.split(&sc.power_control); 28 | //! let timer_output_pin = portb.pb0.into_af_push_pull::(&mut portb.control); 29 | //! let uart_tx_pin = portb 30 | //! .pb1 31 | //! .into_af_open_drain::(&mut portb.control); 32 | //! let blue_led = portb.pb2.into_push_pull_output(); 33 | //! let button = portb.pb3.into_pull_up_input(); 34 | //! # } 35 | //! ``` 36 | 37 | pub use tm4c_hal::gpio::*; 38 | 39 | use crate::{ 40 | bb, 41 | hal::digital::{InputPin, OutputPin, StatefulOutputPin}, 42 | sysctl, 43 | }; 44 | use core::marker::PhantomData; 45 | use tm4c_hal::gpio_macro; 46 | 47 | /// Extension trait to split a GPIO peripheral in independent pins and registers 48 | pub trait GpioExt { 49 | /// The to split the GPIO into 50 | type Parts; 51 | 52 | /// Splits the GPIO block into independent pins and registers 53 | fn split(self, power_control: &sysctl::PowerControl) -> Self::Parts; 54 | } 55 | 56 | gpio_macro!(tm4c129x, GPIO_PORTA_AHB, gpioa, GpioA, PAx, [ 57 | PA0: (pa0, 0, Tristate), 58 | PA1: (pa1, 1, Tristate), 59 | PA2: (pa2, 2, Tristate), 60 | PA3: (pa3, 3, Tristate), 61 | PA4: (pa4, 4, Tristate), 62 | PA5: (pa5, 5, Tristate), 63 | PA6: (pa6, 6, Tristate), 64 | PA7: (pa7, 7, Tristate), 65 | ]); 66 | 67 | gpio_macro!(tm4c129x, GPIO_PORTB_AHB, gpiob, GpioB, PBx, [ 68 | PB0: (pb0, 0, Tristate), 69 | PB1: (pb1, 1, Tristate), 70 | PB2: (pb2, 2, Tristate), 71 | PB3: (pb3, 3, Tristate), 72 | PB4: (pb4, 4, Tristate), 73 | PB5: (pb5, 5, Tristate), 74 | // PB6 and PB7 don't exist 75 | ]); 76 | 77 | gpio_macro!(tm4c129x, GPIO_PORTC_AHB, gpioc, GpioC, PCx, [ 78 | PC0: (pc0, 0, Locked), // JTAG/SWD pin 79 | PC1: (pc1, 1, Locked), // JTAG/SWD pin 80 | PC2: (pc2, 2, Locked), // JTAG/SWD pin 81 | PC3: (pc3, 3, Locked), // JTAG/SWD pin 82 | PC4: (pc4, 4, Tristate), 83 | PC5: (pc5, 5, Tristate), 84 | PC6: (pc6, 6, Tristate), 85 | PC7: (pc7, 7, Tristate), 86 | ]); 87 | 88 | gpio_macro!(tm4c129x, GPIO_PORTD_AHB, gpiod, GpioD, PDx, [ 89 | PD0: (pd0, 0, Tristate), 90 | PD1: (pd1, 1, Tristate), 91 | PD2: (pd2, 2, Tristate), 92 | PD3: (pd3, 3, Tristate), 93 | PD4: (pd4, 4, Tristate), 94 | PD5: (pd5, 5, Tristate), 95 | PD6: (pd6, 6, Tristate), 96 | PD7: (pd7, 7, Locked), // GPIO pin 97 | ]); 98 | 99 | gpio_macro!(tm4c129x, GPIO_PORTE_AHB, gpioe, GpioE, PEx, [ 100 | PE0: (pe0, 0, Tristate), 101 | PE1: (pe1, 1, Tristate), 102 | PE2: (pe2, 2, Tristate), 103 | PE3: (pe3, 3, Tristate), 104 | PE4: (pe4, 4, Tristate), 105 | PE5: (pe5, 5, Tristate), 106 | // PE6 and PE7 don't exist 107 | ]); 108 | 109 | gpio_macro!(tm4c129x, GPIO_PORTF_AHB, gpiof, GpioF, PFx, [ 110 | PF0: (pf0, 0, Tristate), 111 | PF1: (pf1, 1, Tristate), 112 | PF2: (pf2, 2, Tristate), 113 | PF3: (pf3, 3, Tristate), 114 | PF4: (pf4, 4, Tristate), 115 | // PF5, PF6 and PF7 don't exist 116 | ]); 117 | 118 | gpio_macro!(tm4c129x, GPIO_PORTG_AHB, gpiog, GpioG, PGx, [ 119 | PG0: (pg0, 0, Tristate), 120 | PG1: (pg1, 1, Tristate), 121 | // PG2 through PG7 don't exist 122 | ]); 123 | 124 | gpio_macro!(tm4c129x, GPIO_PORTH_AHB, gpioh, GpioH, PHx, [ 125 | PH0: (ph0, 0, Tristate), 126 | PH1: (ph1, 1, Tristate), 127 | PH2: (ph2, 2, Tristate), 128 | PH3: (ph3, 3, Tristate), 129 | // PH4 through PG7 don't exist 130 | ]); 131 | 132 | gpio_macro!(tm4c129x, GPIO_PORTJ_AHB, gpioj, GpioJ, PJx, [ 133 | PJ0: (pj0, 0, Tristate), 134 | PJ1: (pj1, 1, Tristate), 135 | // PJ2 through PJ7 don't exist 136 | ]); 137 | 138 | gpio_macro!(tm4c129x, GPIO_PORTK, gpiok, GpioK, PKx, [ 139 | PK0: (pk0, 0, Tristate), 140 | PK1: (pk1, 1, Tristate), 141 | PK2: (pk2, 2, Tristate), 142 | PK3: (pk3, 3, Tristate), 143 | PK4: (pk4, 4, Tristate), 144 | PK5: (pk5, 5, Tristate), 145 | PK6: (pk6, 6, Tristate), 146 | PK7: (pk7, 7, Tristate), 147 | ]); 148 | 149 | gpio_macro!(tm4c129x, GPIO_PORTL, gpiol, GpioL, PNL, [ 150 | PL0: (pl0, 0, Tristate), 151 | PL1: (pl1, 1, Tristate), 152 | PL2: (pl2, 2, Tristate), 153 | PL3: (pl3, 3, Tristate), 154 | PL4: (pl4, 4, Tristate), 155 | PL5: (pl5, 5, Tristate), 156 | PL6: (pl6, 6, Tristate), 157 | PL7: (pl7, 7, Tristate), 158 | ]); 159 | 160 | gpio_macro!(tm4c129x, GPIO_PORTM, gpiom, GpioM, PMx, [ 161 | PM0: (pm0, 0, Tristate), 162 | PM1: (pm1, 1, Tristate), 163 | PM2: (pm2, 2, Tristate), 164 | PM3: (pm3, 3, Tristate), 165 | PM4: (pm4, 4, Tristate), 166 | PM5: (pm5, 5, Tristate), 167 | PM6: (pm6, 6, Tristate), 168 | PM7: (pm7, 7, Tristate), 169 | ]); 170 | 171 | gpio_macro!(tm4c129x, GPIO_PORTN, gpion, GpioN, PNx, [ 172 | PN0: (pn0, 0, Tristate), 173 | PN1: (pn1, 1, Tristate), 174 | PN2: (pn2, 2, Tristate), 175 | PN3: (pn3, 3, Tristate), 176 | PN4: (pn4, 4, Tristate), 177 | PN5: (pn5, 5, Tristate), 178 | PN6: (pn6, 6, Tristate), 179 | PN7: (pn7, 7, Tristate), 180 | ]); 181 | 182 | gpio_macro!(tm4c129x, GPIO_PORTP, gpiop, GpioP, PPx, [ 183 | PP0: (pp0, 0, Tristate), 184 | PP1: (pp1, 1, Tristate), 185 | PP2: (pp2, 2, Tristate), 186 | PP3: (pp3, 3, Tristate), 187 | PP4: (pp4, 4, Tristate), 188 | PP5: (pp5, 5, Tristate), 189 | // PP6 and PP7 don't exist 190 | ]); 191 | 192 | gpio_macro!(tm4c129x, GPIO_PORTQ, gpioq, GpioQ, PQx, [ 193 | PQ0: (pq0, 0, Tristate), 194 | PQ1: (pq1, 1, Tristate), 195 | PQ2: (pq2, 2, Tristate), 196 | PQ3: (pq3, 3, Tristate), 197 | PQ4: (pq4, 4, Tristate), 198 | // PQ5, PQ6 and PQ7 don't exist 199 | ]); 200 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/hib.rs: -------------------------------------------------------------------------------- 1 | //! A wrapper for the HIB (Hibernation) peripheral 2 | 3 | use crate::sysctl; 4 | 5 | /// Which source to use for the HIB clock 6 | pub enum Source { 7 | /// HIB clock is from an external crystal 8 | ExternalCrystal, 9 | /// HIB clock is from an external oscillator 10 | ExternalOscillator, 11 | /// HIB clock is from the internal low-frequency oscillator 12 | LowFrequencyInternalOscillator, 13 | } 14 | 15 | /// A wrapper around the HIB (Hibernation) peripheral 16 | pub struct Hib { 17 | hib: tm4c129x::HIB, 18 | } 19 | 20 | impl Hib { 21 | /// Initialize the HIB peripheral, using a clock from `source` 22 | pub fn hib(hib: tm4c129x::HIB, source: Source, _pc: &sysctl::PowerControl) -> Self { 23 | hib.ctl.write(|w| { 24 | match source { 25 | Source::ExternalCrystal => w.oscsel().clear_bit().oscbyp().clear_bit(), 26 | Source::ExternalOscillator => w.oscsel().clear_bit().oscbyp().set_bit(), 27 | Source::LowFrequencyInternalOscillator => w.oscsel().set_bit().oscbyp().clear_bit(), 28 | }; 29 | 30 | w.clk32en().set_bit(); 31 | 32 | w 33 | }); 34 | 35 | while hib.ctl.read().wrc().bit_is_clear() {} 36 | hib.ctl.write(|w| { 37 | match source { 38 | Source::ExternalCrystal => w.oscsel().clear_bit().oscbyp().clear_bit(), 39 | Source::ExternalOscillator => w.oscsel().clear_bit().oscbyp().set_bit(), 40 | Source::LowFrequencyInternalOscillator => w.oscsel().set_bit().oscbyp().clear_bit(), 41 | }; 42 | 43 | w.oscdrv().set_bit(); 44 | w.clk32en().set_bit(); 45 | w.rtcen().set_bit(); 46 | 47 | w 48 | }); 49 | while hib.ctl.read().wrc().bit_is_clear() {} 50 | 51 | Hib { hib } 52 | } 53 | 54 | /// Get the current time, in units of (seconds, subseconds), where a 55 | /// subsecond is 1/32768 seconds 56 | pub fn get_time(&self) -> (u32, u16) { 57 | loop { 58 | let seconds = self.hib.rtcc.read().bits(); 59 | let subsec = self.hib.rtcss.read().rtcssc().bits(); 60 | 61 | if seconds == self.hib.rtcc.read().bits() { 62 | return (seconds, subsec); 63 | } 64 | } 65 | } 66 | 67 | /// Get the current time in milliseconds 68 | pub fn get_millis(&self) -> u64 { 69 | let (seconds, subsec) = self.get_time(); 70 | let seconds: u64 = seconds.into(); 71 | let subsec: u64 = subsec.into(); 72 | 73 | let millis: u64 = subsec * 1000 / 32_768; 74 | seconds * 1000 + millis 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Inter-Integrated Circuit (I2C) bus 2 | 3 | use crate::{ 4 | gpio::*, 5 | hal::blocking::i2c::{Read, Write, WriteRead}, 6 | sysctl::{self, Clocks}, 7 | time::Hertz, 8 | Sealed, 9 | }; 10 | 11 | use cortex_m::asm::delay; 12 | use tm4c129x::{I2C0, I2C1, I2C2, I2C3}; 13 | 14 | pub use tm4c_hal::i2c::Error; 15 | pub use tm4c_hal::{i2c_busy_wait, i2c_hal, i2c_pins}; 16 | 17 | /// I2C peripheral operating in master mode 18 | pub struct I2c { 19 | /// Underlying I2C peripheral 20 | pub i2c: I2C, 21 | /// Underlying GPIO pins used by peripheral 22 | pub pins: PINS, 23 | } 24 | 25 | /// SCL pin 26 | pub trait SclPin: Sealed {} 27 | 28 | /// SDA pin 29 | pub trait SdaPin: Sealed {} 30 | 31 | i2c_pins!(I2C0, scl: [(gpiob::PB2, AF2)], sda: [(gpiob::PB3, AF2)],); 32 | i2c_pins!(I2C1, scl: [(gpiog::PG0, AF2)], sda: [(gpiog::PG1, AF2)],); 33 | i2c_pins!(I2C2, 34 | scl: [(gpiol::PL1, AF2),(gpiop::PP5, AF2), (gpion::PN5, AF3)], 35 | sda: [(gpiol::PL0, AF2), (gpion::PN4, AF3)], 36 | ); 37 | 38 | i2c_hal! { 39 | I2C0: (I2c0, i2c0), 40 | I2C1: (I2c1, i2c1), 41 | I2C2: (I2c2, i2c2), 42 | I2C3: (I2c3, i2c3), 43 | } 44 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! HAL for the tm4c129x family of microcontrollers 2 | //! 3 | //! This is an implementation of the [`embedded-hal`] traits for the tm4c129x 4 | //! family of microcontrollers. 5 | //! 6 | //! [`embedded-hal`]: https://github.com/japaric/embedded-hal 7 | //! 8 | //! # Usage 9 | //! 10 | //! To build applications (binary crates) using this crate follow the 11 | //! [cortex-m-quickstart] instructions and add this crate as a dependency in 12 | //! step number 5 and make sure you enable the "rt" Cargo feature of this crate. 13 | //! 14 | //! [cortex-m-quickstart]: https://docs.rs/cortex-m-quickstart/~0.2.3 15 | //! 16 | //! # Examples 17 | //! 18 | //! Examples of *using* these abstractions like these can be found in the 19 | //! documentation of the [`f3`] crate. 20 | //! 21 | //! [`f3`]: https://docs.rs/f3/~0.5.1 22 | 23 | #![no_std] 24 | #![deny(missing_docs)] 25 | #![deny(warnings)] 26 | #![allow(deprecated)] 27 | 28 | pub use tm4c129x::{self, CorePeripherals, Peripherals}; 29 | pub use tm4c_hal::{bb, delay, time}; 30 | 31 | // Enable use of interrupt macro 32 | #[cfg(feature = "rt")] 33 | pub use crate::tm4c129x::interrupt; 34 | 35 | use sealed::Sealed; 36 | mod sealed { 37 | // To prevent implementation of `*Pin` traits on arbitrary types 38 | pub trait Sealed {} 39 | 40 | impl Sealed for () {} 41 | } 42 | 43 | pub mod gpio; 44 | pub mod hib; 45 | pub mod i2c; 46 | pub mod prelude; 47 | pub mod serial; 48 | // pub mod spi; 49 | pub mod sysctl; 50 | 51 | use embedded_hal as hal; 52 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! Prelude 2 | 3 | #[rustfmt::skip] 4 | pub use crate::{ 5 | gpio::GpioExt as _, 6 | hal::prelude::*, 7 | sysctl::SysctlExt, 8 | time::U32Ext, 9 | }; 10 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial 2 | 3 | // uart_hal_macro can be called with too-many arguments 4 | #![allow(clippy::too_many_arguments)] 5 | 6 | use core::{fmt, marker::PhantomData}; 7 | 8 | use crate::{ 9 | gpio::*, 10 | hal::{prelude::*, serial}, 11 | sysctl::{self, Clocks}, 12 | time::Bps, 13 | }; 14 | use nb::{self, block}; 15 | use void::Void; 16 | 17 | pub use tm4c129x::{UART0, UART1, UART2, UART3, UART4, UART5, UART6, UART7}; 18 | pub use tm4c_hal::serial::NewlineMode; 19 | use tm4c_hal::{uart_hal_macro, uart_pin_macro}; 20 | 21 | /// Serial abstraction 22 | pub struct Serial { 23 | uart: UART, 24 | tx_pin: TX, 25 | rx_pin: RX, 26 | rts_pin: RTS, 27 | cts_pin: CTS, 28 | nl_mode: NewlineMode, 29 | } 30 | 31 | /// Serial receiver 32 | pub struct Rx { 33 | _uart: PhantomData, 34 | pin: RX, 35 | flow_pin: CTS, 36 | } 37 | 38 | /// Serial transmitter 39 | pub struct Tx { 40 | uart: UART, 41 | pin: TX, 42 | flow_pin: RTS, 43 | nl_mode: NewlineMode, 44 | } 45 | 46 | uart_pin_macro!(UART0, 47 | cts: [(gpioh::PH1, AF1), (gpiom::PM4, AF1), (gpiob::PB4, AF1)], 48 | // dcd: [(gpioh::PH2, AF1), (gpiom::PM5, AF1), (gpiop::PP3, AF2)], 49 | // dsr: [(gpioh::PH3, AF1), (gpiom::PM6, AF1), (gpiop::PP4, AF2)], 50 | // dtr: [(gpiop::PP2, AF1)], 51 | // ri: [(gpiok::PK7, AF1), (gpiom::PM7, AF1)], 52 | rts: [(gpioh::PH0, AF1), (gpiob::PB5, AF1)], 53 | rx: [(gpioa::PA0, AF1)], 54 | tx: [(gpioa::PA1, AF1)], 55 | ); 56 | 57 | uart_pin_macro!(UART1, 58 | cts: [(gpion::PN1, AF1), (gpiop::PP3, AF1)], 59 | // dcd: [(gpioe::PE2, AF1), (gpion::PN2, AF1)], 60 | // dsr: [(gpioe::PE1, AF1), (gpion::PN3, AF1)], 61 | // dtr: [(gpioe::PE3, AF1), (gpion::PN4, AF1)], 62 | // ri: [(gpioe::PE4, AF1), (gpion::PN5, AF1)], 63 | rts: [(gpioe::PE0, AF1), (gpion::PN0, AF1)], 64 | rx: [(gpiob::PB0, AF1), (gpioq::PQ4, AF1)], 65 | tx: [(gpiob::PB1, AF1)], 66 | ); 67 | 68 | uart_pin_macro!(UART2, 69 | cts: [(gpiod::PD7, AF1), (gpion::PN3, AF2)], 70 | rts: [(gpiod::PD6, AF1), (gpion::PN2, AF2)], 71 | rx: [(gpioa::PA6, AF1), (gpiod::PD4, AF1)], 72 | tx: [(gpioa::PA7, AF1), (gpiod::PD5, AF1)], 73 | ); 74 | 75 | uart_pin_macro!(UART3, 76 | cts: [(gpiop::PP5, AF1), (gpion::PN5, AF2)], 77 | rts: [(gpiop::PP4, AF1), (gpion::PN4, AF2)], 78 | rx: [(gpioa::PA4, AF1), (gpioj::PJ0, AF1)], 79 | tx: [(gpioa::PA5, AF1), (gpioj::PJ1, AF1)], 80 | ); 81 | 82 | uart_pin_macro!(UART4, 83 | cts: [(gpiok::PK3, AF1)], 84 | rts: [(gpiok::PK2, AF1)], 85 | rx: [(gpioa::PA2, AF1), (gpiok::PK0, AF1)], 86 | tx: [(gpioa::PA3, AF1), (gpiok::PK1, AF1)], 87 | ); 88 | 89 | uart_pin_macro!(UART5, 90 | cts: [], 91 | rts: [], 92 | rx: [(gpioc::PC6, AF1)], 93 | tx: [(gpioc::PC7, AF1)], 94 | ); 95 | 96 | uart_pin_macro!(UART6, 97 | cts: [], 98 | rts: [], 99 | rx: [(gpiop::PP0, AF1)], 100 | tx: [(gpiop::PP1, AF1)], 101 | ); 102 | 103 | uart_pin_macro!(UART7, 104 | cts: [], 105 | rts: [], 106 | rx: [(gpioc::PC4, AF1)], 107 | tx: [(gpioc::PC5, AF1)], 108 | ); 109 | 110 | uart_hal_macro! { 111 | UART0: (Uart0, uart0), 112 | UART1: (Uart1, uart1), 113 | UART2: (Uart2, uart2), 114 | UART3: (Uart3, uart3), 115 | UART4: (Uart4, uart4), 116 | UART5: (Uart5, uart5), 117 | UART6: (Uart6, uart6), 118 | UART7: (Uart7, uart7), 119 | } 120 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/spi.rs: -------------------------------------------------------------------------------- 1 | //! Serial Peripheral Interface (SPI) bus 2 | 3 | pub use crate::hal::spi::{Mode, MODE_0, MODE_1, MODE_2, MODE_3}; 4 | 5 | use crate::{ 6 | gpio::{ 7 | gpioa::{PA2, PA4, PA5}, 8 | gpiob::{PB4, PB6, PB7}, 9 | gpiod::{PD0, PD2, PD3}, 10 | AlternateFunction, OutputMode, AF1, AF2, 11 | }, 12 | hal::spi::{FullDuplex, Phase, Polarity}, 13 | sysctl::{self, Clocks}, 14 | time::Hertz, 15 | Sealed, 16 | }; 17 | 18 | use nb; 19 | use tm4c129x::{SSI0, SSI1, SSI2, SSI3}; 20 | 21 | /// SPI error 22 | #[derive(Debug)] 23 | pub enum Error { 24 | #[doc(hidden)] 25 | _Extensible, 26 | } 27 | 28 | /// SCK pin 29 | pub trait SckPin: Sealed {} 30 | 31 | /// MISO pin 32 | pub trait MisoPin: Sealed {} 33 | 34 | /// MOSI pin 35 | pub unsafe trait MosiPin: Sealed {} 36 | 37 | // SSI0 38 | impl SckPin for PA2> where T: OutputMode {} 39 | impl MisoPin for PA4> where T: OutputMode {} 40 | impl MosiPin for PA5> where T: OutputMode {} 41 | 42 | // SSI1 43 | impl SckPin for PD0> where T: OutputMode {} 44 | impl MisoPin for PD2> where T: OutputMode {} 45 | impl MosiPin for PD3> where T: OutputMode {} 46 | 47 | // SSI2 48 | impl SckPin for PB4> where T: OutputMode {} 49 | impl MisoPin for PB6> where T: OutputMode {} 50 | impl MosiPin for PB7> where T: OutputMode {} 51 | 52 | // SSI3 53 | impl SckPin for PD0> where T: OutputMode {} 54 | impl MisoPin for PD2> where T: OutputMode {} 55 | impl MosiPin for PD3> where T: OutputMode {} 56 | 57 | /// SPI peripheral operating in full duplex master mode 58 | pub struct Spi { 59 | spi: SPI, 60 | pins: PINS, 61 | } 62 | 63 | macro_rules! busy_wait { 64 | ($spi:expr, $flag:ident, $op:ident) => { 65 | loop { 66 | let sr = $spi.sr.read(); 67 | if sr.$flag().$op() { 68 | break; 69 | } 70 | } 71 | }; 72 | } 73 | 74 | macro_rules! hal { 75 | ($($SPIX:ident: ($powerDomain:ident, $spiX:ident),)+) => { 76 | $( 77 | impl Spi<$SPIX, (SCK, MISO, MOSI)> { 78 | /// Configures the SPI peripheral to operate in full duplex master mode 79 | pub fn $spiX( 80 | spi: $SPIX, 81 | pins: (SCK, MISO, MOSI), 82 | mode: Mode, 83 | freq: F, 84 | clocks: &Clocks, 85 | pc: &sysctl::PowerControl, 86 | ) -> Self 87 | where 88 | F: Into, 89 | SCK: SckPin<$SPIX>, 90 | MISO: MisoPin<$SPIX>, 91 | MOSI: MosiPin<$SPIX>, 92 | { 93 | // power up 94 | sysctl::control_power( 95 | pc, sysctl::Domain::$powerDomain, 96 | sysctl::RunMode::Run, sysctl::PowerState::On); 97 | sysctl::reset(pc, sysctl::Domain::$powerDomain); 98 | 99 | // write 0 (reset value) for master operation. 100 | spi.cr1.write(|w| w); 101 | 102 | // SSICC Clock setup 103 | // set to reset value (0 = use system clock) 104 | spi.cc.write(|w| w); 105 | 106 | // Use Moto/SPI & 8bits data size 107 | let scr: u8; 108 | let mut cpsr = 2u32; 109 | let target_bitrate : u32 = clocks.sysclk.0 / freq.into().0; 110 | 111 | // Find solution for 112 | // SSInClk = SysClk / (CPSDVSR * (1 + SCR)) 113 | // with: 114 | // CPSDVSR in [2,254] 115 | // SCR in [0,255] 116 | 117 | loop { 118 | let scr32 = (target_bitrate / cpsr) - 1; 119 | if scr32 < 255 { 120 | scr = scr32 as u8; 121 | break; 122 | } 123 | cpsr += 2; 124 | assert!(cpsr <= 254); 125 | } 126 | 127 | let cpsr = cpsr as u8; 128 | 129 | spi.cpsr.write(|w| unsafe { 130 | w.cpsdvsr().bits(cpsr) 131 | }); 132 | 133 | spi.cr0.modify(|_,w| unsafe { 134 | w.spo().bit(mode.polarity == Polarity::IdleHigh) 135 | .sph().bit(mode.phase == Phase::CaptureOnSecondTransition) 136 | // FIXME: How to use FRFR::MOTO and DSS:: ? 137 | .frf().bits(0) 138 | .dss().bits(0x7) 139 | .scr().bits(scr) 140 | }); 141 | 142 | // Enable peripheral 143 | spi.cr1.write(|w| w.sse().set_bit()); 144 | 145 | Spi { spi, pins } 146 | } 147 | 148 | /// Releases the SPI peripheral and associated pins 149 | pub fn free(self) -> ($SPIX, (SCK, MISO, MOSI)) { 150 | (self.spi, self.pins) 151 | } 152 | } 153 | 154 | impl FullDuplex for Spi<$SPIX, PINS> { 155 | type Error = Error; 156 | 157 | fn read(&mut self) -> nb::Result { 158 | // Receive FIFO Not Empty 159 | if self.spi.sr.read().rne().bit_is_clear() { 160 | Err(nb::Error::WouldBlock) 161 | } else { 162 | let r = self.spi.dr.read().data().bits() as u8; 163 | Ok(r) 164 | } 165 | } 166 | 167 | fn send(&mut self, byte: u8) -> nb::Result<(), Error> { 168 | // Transmit FIFO Not Full 169 | if self.spi.sr.read().tnf().bit_is_clear() { 170 | Err(nb::Error::WouldBlock) 171 | } else { 172 | self.spi.dr.write(|w| unsafe { 173 | w.data().bits(byte.into()) 174 | }); 175 | busy_wait!(self.spi, bsy, bit_is_clear); 176 | Ok(()) 177 | } 178 | } 179 | } 180 | 181 | impl crate::hal::blocking::spi::transfer::Default for Spi<$SPIX, PINS> {} 182 | 183 | impl crate::hal::blocking::spi::write::Default for Spi<$SPIX, PINS> {} 184 | )+ 185 | } 186 | } 187 | 188 | hal! { 189 | SSI0: (Ssi0, spi0), 190 | SSI1: (Ssi1, spi1), 191 | SSI2: (Ssi2, spi2), 192 | SSI3: (Ssi3, spi3), 193 | } 194 | -------------------------------------------------------------------------------- /tm4c129x-hal/src/time.rs: -------------------------------------------------------------------------------- 1 | //! Time units 2 | 3 | pub use tm4c_hal::time::*; 4 | 5 | use crate::sysctl::Clocks; 6 | 7 | impl MonoTimer { 8 | /// Creates a new `Monotonic` timer 9 | pub fn new(mut dwt: DWT, clocks: Clocks) -> Self { 10 | dwt.enable_cycle_counter(); 11 | 12 | // now the CYCCNT counter can't be stopped or resetted 13 | drop(dwt); 14 | 15 | MonoTimer { 16 | frequency: clocks.sysclk(), 17 | } 18 | } 19 | } 20 | --------------------------------------------------------------------------------