├── .cargo └── config.toml ├── .github ├── CODEOWNERS └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── Embed.toml ├── LICENSE ├── README.md ├── build.rs ├── examples ├── adc.rs ├── ctimer_blink.rs ├── ctimer_fade.rs ├── gpio_delay.rs ├── gpio_dynamic.rs ├── gpio_generic.rs ├── gpio_input.rs ├── gpio_simple.rs ├── gpio_sleep.rs ├── gpio_timer.rs ├── i2c_eeprom.rs ├── i2c_master_slave.rs ├── i2c_master_slave_dma.rs ├── i2c_vl53l0x.rs ├── mrt_clock.rs ├── pinint.rs ├── pmu.rs ├── rtic.rs ├── spi_apa102.rs ├── usart.rs └── usart_dma.rs ├── lpc8xx-hal.sublime-project ├── memory_16_4.x ├── memory_32_8.x ├── memory_64_16.x ├── openocd.gdb ├── openocd_82x.cfg ├── openocd_84x.cfg ├── rustfmt.toml ├── scripts ├── build.sh └── test.sh ├── src ├── adc.rs ├── clock.rs ├── ctimer │ ├── channel.rs │ ├── gen.rs │ ├── mod.rs │ └── peripheral.rs ├── delay.rs ├── dma │ ├── buffer.rs │ ├── channels.rs │ ├── descriptors.rs │ ├── gen.rs │ ├── mod.rs │ ├── peripheral.rs │ └── transfer.rs ├── gpio.rs ├── i2c │ ├── clock.rs │ ├── error.rs │ ├── instances.rs │ ├── interrupts.rs │ ├── master.rs │ ├── mod.rs │ ├── peripheral.rs │ └── slave.rs ├── lib.rs ├── mrt │ ├── channel.rs │ ├── gen.rs │ ├── mod.rs │ ├── peripheral.rs │ └── ticks.rs ├── pinint │ ├── gen.rs │ ├── interrupt.rs │ ├── mod.rs │ ├── peripheral.rs │ └── traits.rs ├── pins │ ├── gen.rs │ ├── mod.rs │ ├── pin.rs │ ├── state.rs │ └── traits.rs ├── pmu.rs ├── reg_proxy.rs ├── sleep.rs ├── spi │ ├── clock.rs │ ├── dma.rs │ ├── instances.rs │ ├── interrupts.rs │ ├── mod.rs │ └── peripheral.rs ├── swm │ ├── assignment.rs │ ├── fixed_functions.rs │ ├── function_kind.rs │ ├── functions.rs │ ├── handle.rs │ ├── mod.rs │ ├── movable_functions.rs │ ├── peripheral.rs │ └── state.rs ├── syscon │ ├── clock_source.rs │ ├── frg.rs │ └── mod.rs ├── usart │ ├── clock.rs │ ├── flags.rs │ ├── instances.rs │ ├── mod.rs │ ├── peripheral.rs │ ├── rx.rs │ ├── settings.rs │ ├── state.rs │ └── tx.rs └── wkt.rs ├── test-suite ├── .cargo │ └── config.toml ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ ├── Cargo.toml │ └── tests │ └── test.rs └── tests ├── compile-fail └── swm │ ├── assign-function-to-multiple-pins.rs │ ├── assign-function-to-multiple-pins.stderr │ ├── assign-multiple-output-functions.rs │ ├── assign-multiple-output-functions.stderr │ ├── unassign-function-from-wrong-pin.rs │ ├── unassign-function-from-wrong-pin.stderr │ ├── unassign-unassigned-input-function.rs │ ├── unassign-unassigned-input-function.stderr │ ├── unassign-unassigned-output-function.rs │ └── unassign-unassigned-output-function.stderr └── compiletest.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv6m-none-eabi" 3 | 4 | [target.thumbv6m-none-eabi] 5 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 6 | rustflags = [ 7 | "-C", "link-arg=-Tlink.x", 8 | ] 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Set the `lpc8xx` team as code owner of this repository. A review request will 2 | # automatically be sent for every non-draft pull request. 3 | * @lpc-rs/lpc8xx 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | 6 | name: Continuous integration 7 | 8 | jobs: 9 | ci: 10 | runs-on: ubuntu-latest 11 | continue-on-error: ${{ matrix.experimental }} 12 | strategy: 13 | matrix: 14 | rust: 15 | - stable 16 | experimental: [false] 17 | include: 18 | - rust: nightly 19 | experimental: true 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions-rs/toolchain@v1 24 | with: 25 | profile: minimal 26 | toolchain: ${{ matrix.rust }} 27 | target: thumbv6m-none-eabi 28 | override: true 29 | 30 | - name: Regular build 31 | run: ./scripts/build.sh 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo 2 | target 3 | Cargo.lock 4 | 5 | # Sublime Text 6 | *.sublime-workspace 7 | 8 | # OpenOCD 9 | openocd.log 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": [ 3 | "845-rt" 4 | ], 5 | "rust-analyzer.checkOnSave.allTargets": false, 6 | "editor.formatOnSave": true 7 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to LPC8xx HAL 2 | 3 | Thank you for considering to work on LPC8xx HAL. This document will give you some pointers and explain the guidelines that you need to follow. 4 | 5 | ## Opening issues 6 | 7 | If you found a problem, please open an issue on the [GitHub repository]. If you're not sure whether you found a real problem or not, just open an issue anyway. We'd rather close a few invalid issues than miss anything relevant. 8 | 9 | ## Contributing changes 10 | 11 | If you want to contribute a change to LPC8xx HAL, please open a pull request on the [GitHub repository]. The best way to open a pull request is usually to just push a branch to your fork, then click the button that appears near the top of your fork's GitHub page. 12 | 13 | If you're having any problems with completing your change, feel free to open a pull request anyway and ask any questions there. We're happy to help you get your changes across the finish line. 14 | 15 | ## Release Procedure 16 | 17 | This section is intended for project maintainers only. It assumes that you can push to the repository (here called `upstream`, but primarily work on your own fork (`origin`), 18 | 19 | 1. Check out feature branch for the release (replace x.y.z with actual version) 20 | ``` 21 | $ git checkout -b release-x.y.z 22 | ``` 23 | 24 | 2. Push feature branch to your fork (required for the next steps to work) 25 | ``` 26 | $ git push -u origin release-x.y.z 27 | ``` 28 | 29 | 3. Update changelog 30 | 31 | 4. Update versions in README.md, if version bump is major or minor 32 | 33 | 5. Do cargo-release dry run, review its actions 34 | ``` 35 | $ cargo release --level major|minor|patch --dry-run 36 | ``` 37 | 38 | 6. Run cargo-release 39 | ``` 40 | $ cargo release --level major|minor|patch 41 | ``` 42 | 43 | 7. Open pull request, review it, merge it 44 | 45 | 8. Push the tag that cargo-release created to `upstream` 46 | ``` 47 | git checkout master 48 | git pull upstream master 49 | git push --tag upstream master 50 | ``` 51 | 52 | 53 | That's it! If anything about this document is unclear, feel free to open an issue. If you have questions regarding a pull request that you're working on, just open the pull request and ask your questions there. 54 | 55 | [GitHub repository]: https://github.com/lpc-rs/lpc8xx-hal 56 | [clog]: https://crates.io/crates/clog-cli 57 | [GitCop]: https://gitcop.com/ 58 | -------------------------------------------------------------------------------- /Embed.toml: -------------------------------------------------------------------------------- 1 | # Common configuration. The target-specific configurations below interit from 2 | # this implicitly. 3 | 4 | [default.rtt] 5 | enabled = true 6 | 7 | 8 | # Configuration for LPCXpresso824-MAX development board. 9 | 10 | [lpc82x.probe] 11 | usb_vid = "0d28" 12 | usb_pid = "0204" 13 | 14 | [lpc82x.general] 15 | # The schematics say the chip on the board is an "LPC824M201FHI33", but I'm 16 | # guessing this is a typo, and in any case, the following is probably close 17 | # enough. 18 | chip = "LPC824M201JHI33" 19 | 20 | 21 | # Configuration for the LPC845-BRK development board. 22 | 23 | [lpc845.probe] 24 | usb_vid = "1fc9" 25 | usb_pid = "0132" 26 | 27 | [lpc845.general] 28 | chip = "LPC845M301JHI48" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Hanno Braun and contributors 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LPC8xx HAL [![crates.io](https://img.shields.io/crates/v/lpc8xx-hal.svg)](https://crates.io/crates/lpc8xx-hal) [![Documentation](https://docs.rs/lpc8xx-hal/badge.svg)](https://docs.rs/lpc8xx-hal) [![Build Status](https://travis-ci.com/lpc-rs/lpc8xx-hal.svg?branch=master)](https://travis-ci.com/lpc-rs/lpc8xx-hal) 2 | 3 | ## Introduction 4 | 5 | Hardware Abstraction Layer (HAL) for the [NXP LPC800] series of microcontrollers, written in the [Rust] programming language. The [LPC82x] and [LPC845] are currently supported. LPC8xx HAL provides a high-level interface to the features of LPC800 MCUs, that is safe, convenient, and efficient. 6 | 7 | LPC8xx HAL leverages Rust's type system to prevent common mistakes. Things like attempting to use a peripheral that has not been properly initialized, or attempting to assign conflicting functions to the same pin, will all result in compile-time errors. 8 | 9 | This crate is an implementation of [embedded-hal]. Please consider if you can make your code platform-independent, by depending on [embedded-hal] instead of this library. 10 | 11 | [NXP LPC800]: https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc800-cortex-m0-plus-:MC_71785 12 | [LPC82x]: https://www.nxp.com/products/processors-and-microcontrollers/arm-based-processors-and-mcus/lpc-cortex-m-mcus/lpc800-series-cortex-m0-plus-mcus/low-cost-microcontrollers-mcus-based-on-arm-cortex-m0-plus-cores:LPC82X 13 | [LPC845]: https://www.nxp.com/products/processors-and-microcontrollers/arm-based-processors-and-mcus/lpc-cortex-m-mcus/lpc800-series-cortex-m0-plus-mcus/low-cost-microcontrollers-mcus-based-on-arm-cortex-m0-plus-cores:LPC84x 14 | [Rust]: https://www.rust-lang.org/ 15 | [embedded-hal]: https://crates.io/crates/embedded-hal 16 | 17 | 18 | ## Status 19 | 20 | LPC82x HAL is still under heavy development. It is lacking APIs for many peripherals, and the APIs that already exist are mostly incomplete. 21 | 22 | **Do you need a feature that is currently missing? Please [open an issue]!** 23 | 24 | The existing APIs are expected to evolve significantly in the future. API stability is *not* guaranteed, which means future versions might not be compatible with code using the current version. 25 | 26 | 27 | ## Usage 28 | 29 | To include LPC8xx HAL in you project, add the following to your `Cargo.toml`: 30 | 31 | ``` toml 32 | [dependencies] 33 | lpc8xx-hal = "0.9" 34 | ``` 35 | 36 | If you want to use LPC8xx HAL in an application (as opposed to a library), there are additional things that need to be set up. Please refer to the [API Reference] for details. 37 | 38 | To run one of the examples from this repository, please adapt the following command if you're using an LPC845-BRK board: 39 | ``` 40 | cargo embed lpc845 --example gpio_delay --features 845-rt 41 | ``` 42 | 43 | Or adapt the following command if using an LPCXpresso824-MAX board: 44 | ``` 45 | cargo embed lpc82x --example gpio_delay --features 82x-rt 46 | ``` 47 | 48 | 49 | ## Documentation 50 | 51 | The **[API Reference]** should contain everything you need to use this library. If you think that anything's missing, please [open an issue]. 52 | 53 | For functionality that is not yet covered by this crate, you may need to fall back to [`lpc82x-pac`]/[`lpc845-pac`]. Please refer to their respective documentation if necessary. 54 | 55 | The authoritative source on the supported MCUs are their respective user manuals, available from NXP. 56 | 57 | [API Reference]: https://docs.rs/lpc8xx-hal 58 | [`lpc82x-pac`]: https://crates.io/crates/lpc82x-pac 59 | [`lpc845-pac`]: https://crates.io/crates/lpc845-pac 60 | 61 | 62 | ## Help Wanted 63 | 64 | Are you familiar with the LPC8xx family? We need your help, even if you are not using LPC8xx HAL. Some design issues require feedback from people familiar with the hardware and how it is used. Check out the [help wanted] tag on the issue tracker. 65 | 66 | Do you want to contribute to LPC8xx HAL? There's a number of [good first issues] on the issue tracker. If you're unsure about anything, check out our documentation on [how to contribute], or just ask! 67 | 68 | [help wanted]: https://github.com/lpc-rs/lpc8xx-hal/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22 69 | [good first issues]: https://github.com/lpc-rs/lpc8xx-hal/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 70 | [how to contribute]: https://github.com/lpc-rs/lpc8xx-hal/blob/master/CONTRIBUTING.md 71 | 72 | 73 | ## License 74 | 75 | This project is open source software, licensed under the terms of the [Zero Clause BSD License][] (0BSD, for short). This basically means you can do anything with the software, without any restrictions, but you can't hold the authors liable for problems. 76 | 77 | See [LICENSE] for full details. 78 | 79 | [Zero Clause BSD License]: https://opensource.org/licenses/FPL-1.0.0 80 | [LICENSE]: https://github.com/lpc-rs/lpc8xx-hal/blob/master/LICENSE 81 | 82 | 83 | **Supported by [Braun Embedded](https://braun-embedded.com/)** 84 | 85 | 86 | [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues/new 87 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | fs::{self, File}, 4 | io::{self, prelude::*}, 5 | path::PathBuf, 6 | }; 7 | 8 | use crossterm::style::Stylize as _; 9 | 10 | fn main() -> Result<(), Error> { 11 | let target = Target::read(); 12 | 13 | // Nothing this build script does after this point is required when 14 | // generating documentation. 15 | // 16 | // In addition, the docs.rs build environment has a read-only file system, 17 | // meaning an attempt to run this build script will fail the whole build for 18 | // no reason. 19 | if cfg!(feature = "docs") { 20 | return Ok(()); 21 | } 22 | 23 | copy_openocd_config(target)?; 24 | copy_memory_config(target)?; 25 | 26 | println!("cargo:rerun-if-changed=build.rs"); 27 | 28 | Ok(()) 29 | } 30 | 31 | /// Make target-specific configuration available where OpenOCD expects it 32 | fn copy_openocd_config(target: Target) -> Result<(), io::Error> { 33 | let openocd_cfg = match target.family { 34 | Family::LPC82x => &include_bytes!("openocd_82x.cfg")[..], 35 | Family::LPC84x => &include_bytes!("openocd_84x.cfg")[..], 36 | }; 37 | 38 | // These file operations are not using the `OUT_DIR` environment variable on 39 | // purpose. `OUT_DIR` points to a directory within target, whose path even 40 | // contains a hash. This configuration file needs to be referenced from the 41 | // GDB configuration, which can't just ask Cargo where to look for it. 42 | fs::create_dir_all("target")?; 43 | File::create("target/openocd.cfg")?.write_all(openocd_cfg)?; 44 | 45 | println!("cargo:rerun-if-changed=openocd_82x.cfg"); 46 | println!("cargo:rerun-if-changed=openocd_84x.cfg"); 47 | 48 | Ok(()) 49 | } 50 | 51 | /// Make `memory.x` available to dependent crates 52 | fn copy_memory_config(target: Target) -> Result<(), Error> { 53 | let memory_x = match target.sub_family { 54 | SubFamily::LPC822 => include_bytes!("memory_16_4.x").as_ref(), 55 | SubFamily::LPC824 => include_bytes!("memory_32_8.x").as_ref(), 56 | SubFamily::LPC845 => include_bytes!("memory_64_16.x").as_ref(), 57 | }; 58 | 59 | let out_dir = env::var("OUT_DIR")?; 60 | let out_dir = PathBuf::from(out_dir); 61 | 62 | File::create(out_dir.join("memory.x"))?.write_all(memory_x)?; 63 | 64 | // Tell Cargo where to find the file. 65 | println!("cargo:rustc-link-search={}", out_dir.display()); 66 | 67 | println!("cargo:rerun-if-changed=memory_16_4.x"); 68 | println!("cargo:rerun-if-changed=memory_32_8.x"); 69 | println!("cargo:rerun-if-changed=memory_64_16.x"); 70 | 71 | Ok(()) 72 | } 73 | 74 | #[derive(Clone, Copy)] 75 | struct Target { 76 | family: Family, 77 | sub_family: SubFamily, 78 | } 79 | 80 | impl Target { 81 | fn read() -> Self { 82 | let (family, sub_family) = Family::read(); 83 | 84 | Self { family, sub_family } 85 | } 86 | } 87 | 88 | #[derive(Clone, Copy)] 89 | enum Family { 90 | LPC82x, 91 | LPC84x, 92 | } 93 | 94 | impl Family { 95 | fn read() -> (Self, SubFamily) { 96 | let f82x = cfg!(feature = "82x"); 97 | 98 | let s822 = cfg!(feature = "822"); 99 | let s824 = cfg!(feature = "824"); 100 | let s845 = cfg!(feature = "845"); 101 | 102 | match (f82x, s822, s824, s845) { 103 | (true, false, false, false) => { 104 | warn_unspecific_selection(); 105 | (Family::LPC82x, SubFamily::LPC822) 106 | } 107 | (true, true, false, false) => { 108 | (Family::LPC82x, SubFamily::LPC822) 109 | } 110 | (true, false, true, false) => { 111 | (Family::LPC82x, SubFamily::LPC824) 112 | } 113 | (false, false, false, true) => { 114 | (Family::LPC84x, SubFamily::LPC845) 115 | } 116 | 117 | (false, false, false, false) => { 118 | error("You must select a target. 119 | 120 | If you added LPC8xx HAL as a dependency to your crate, you can select a target by enabling the respective feature in `Cargo.toml`. 121 | 122 | If you're running an example from the repository, select a target by passing the desired target as a command-line argument, for example `--features=824m201jhi33`. 123 | 124 | 125 | Please refer to the documentation for more details." 126 | ) 127 | } 128 | _ => { 129 | error( 130 | "You can not select more than one target." 131 | ) 132 | } 133 | } 134 | } 135 | } 136 | 137 | #[derive(Clone, Copy)] 138 | enum SubFamily { 139 | LPC822, 140 | LPC824, 141 | LPC845, 142 | } 143 | 144 | #[derive(Debug)] 145 | enum Error { 146 | Env(env::VarError), 147 | Io(io::Error), 148 | } 149 | 150 | impl From for Error { 151 | fn from(error: env::VarError) -> Self { 152 | Self::Env(error) 153 | } 154 | } 155 | 156 | impl From for Error { 157 | fn from(error: io::Error) -> Self { 158 | Self::Io(error) 159 | } 160 | } 161 | 162 | fn error(message: &str) -> ! { 163 | panic!("\n\n\n{}\n\n\n", message.bold().red(),); 164 | } 165 | 166 | fn warn_unspecific_selection() { 167 | if !cfg!(feature = "no-target-warning") { 168 | println!( 169 | "cargo:warning=You have selected a family (e.g. LPC82x), but not a specific target within that family. Your application will only be able to use the hardware resources available on all targets of that family, while your specific target might have more peripherals or memory available.", 170 | ); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /examples/adc.rs: -------------------------------------------------------------------------------- 1 | //! ADC example for lpc845 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | extern crate panic_rtt_target; 7 | 8 | use core::fmt::Write; 9 | use nb::block; 10 | 11 | use lpc8xx_hal::{ 12 | cortex_m_rt::entry, delay::Delay, prelude::*, 13 | syscon::clock_source::AdcClock, usart, CorePeripherals, Peripherals, 14 | }; 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | rtt_target::rtt_init_print!(); 19 | 20 | let cp = CorePeripherals::take().unwrap(); 21 | let p = Peripherals::take().unwrap(); 22 | 23 | let mut delay = Delay::new(cp.SYST); 24 | let swm = p.SWM.split(); 25 | let mut syscon = p.SYSCON.split(); 26 | 27 | let mut handle = swm.handle.enable(&mut syscon.handle); // SWM isn't enabled by default on LPC845. 28 | 29 | // Set baud rate to 115200 baud 30 | let clock_config = usart::Clock::new_with_baudrate(115200); 31 | 32 | let tx_pin = p.pins.pio0_25.into_swm_pin(); 33 | let rx_pin = p.pins.pio0_24.into_swm_pin(); 34 | 35 | let (u0_rxd, _) = swm.movable_functions.u0_rxd.assign(rx_pin, &mut handle); 36 | let (u0_txd, _) = swm.movable_functions.u0_txd.assign(tx_pin, &mut handle); 37 | 38 | let mut serial = p.USART0.enable_async( 39 | &clock_config, 40 | &mut syscon.handle, 41 | u0_rxd, 42 | u0_txd, 43 | usart::Settings::default(), 44 | ); 45 | 46 | let adc_clock = AdcClock::new_default(); 47 | let mut adc = p.ADC.enable(&adc_clock, &mut syscon.handle); 48 | 49 | let (mut adc_pin, _) = swm 50 | .fixed_functions 51 | .adc_0 52 | .assign(p.pins.pio0_7.into_swm_pin(), &mut handle); 53 | 54 | loop { 55 | let adc_value = 56 | block! {adc.read(&mut adc_pin)}.expect("Read should never fail"); 57 | write!(serial, "{}\n", adc_value).expect("Write should never fail"); 58 | delay.delay_ms(100u8); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/ctimer_blink.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m_rt::entry, ctimer::Channels123, delay::Delay, prelude::*, 8 | CorePeripherals, Peripherals, 9 | }; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | rtt_target::rtt_init_print!(); 14 | 15 | // Get access to the device's peripherals. Since only one instance of this 16 | // struct can exist, the call to `take` returns an `Option`. 17 | // If we tried to call the method a second time, it would return `None`, but 18 | // we're only calling it the one time here, so we can safely `unwrap` the 19 | // `Option` without causing a panic. 20 | let cp = CorePeripherals::take().unwrap(); 21 | let p = Peripherals::take().unwrap(); 22 | 23 | // Initialize the APIs of the peripherals we need. 24 | let swm = p.SWM.split(); 25 | let mut delay = Delay::new(cp.SYST); 26 | let mut syscon = p.SYSCON.split(); 27 | 28 | let mut handle = swm.handle.enable(&mut syscon.handle); 29 | 30 | // Select pin for the RGB LED 31 | let green = p.pins.pio1_0.into_swm_pin(); 32 | let blue = p.pins.pio1_1.into_swm_pin(); 33 | let red = p.pins.pio1_2.into_swm_pin(); 34 | 35 | // Configure the LED pins. The API tracks the state of pins at compile time, 36 | // to prevent any mistakes. 37 | let (red, _) = swm.movable_functions.t0_mat0.assign(red, &mut handle); 38 | let (green, _) = swm.movable_functions.t0_mat1.assign(green, &mut handle); 39 | let (blue, _) = swm.movable_functions.t0_mat2.assign(blue, &mut handle); 40 | 41 | const MAX_PERIOD: u32 = 12_000_000; 42 | const MIN_PERIOD: u32 = MAX_PERIOD / 12; 43 | 44 | let periods = (MIN_PERIOD..MAX_PERIOD).step_by(MIN_PERIOD as usize); 45 | 46 | let mut ctimer = p 47 | .CTIMER0 48 | .enable(MAX_PERIOD, 0, &mut syscon.handle) 49 | .attach(red) 50 | .attach(green) 51 | .attach(blue); 52 | 53 | loop { 54 | for period in periods.clone().rev() { 55 | ctimer.set_period(period); 56 | 57 | ctimer.set_duty(Channels123::Channel1, period / 8); 58 | ctimer.set_duty(Channels123::Channel2, period / 4); 59 | ctimer.set_duty(Channels123::Channel3, period / 2); 60 | 61 | delay.delay_ms(period / 12_000); 62 | } 63 | for period in periods.clone() { 64 | ctimer.set_period(period); 65 | 66 | ctimer.set_duty(Channels123::Channel1, period / 8); 67 | ctimer.set_duty(Channels123::Channel2, period / 4); 68 | ctimer.set_duty(Channels123::Channel3, period / 2); 69 | 70 | delay.delay_ms(period / 12_000); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/ctimer_fade.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m_rt::entry, delay::Delay, prelude::*, CorePeripherals, Peripherals, 8 | }; 9 | 10 | #[entry] 11 | fn main() -> ! { 12 | rtt_target::rtt_init_print!(); 13 | 14 | // Get access to the device's peripherals. Since only one instance of this 15 | // struct can exist, the call to `take` returns an `Option`. 16 | // If we tried to call the method a second time, it would return `None`, but 17 | // we're only calling it the one time here, so we can safely `unwrap` the 18 | // `Option` without causing a panic. 19 | let cp = CorePeripherals::take().unwrap(); 20 | let p = Peripherals::take().unwrap(); 21 | 22 | // Initialize the APIs of the peripherals we need. 23 | let swm = p.SWM.split(); 24 | let mut delay = Delay::new(cp.SYST); 25 | let mut syscon = p.SYSCON.split(); 26 | 27 | let mut handle = swm.handle.enable(&mut syscon.handle); 28 | 29 | // Select pin for the RGB LED 30 | let green = p.pins.pio1_0.into_swm_pin(); 31 | let blue = p.pins.pio1_1.into_swm_pin(); 32 | let red = p.pins.pio1_2.into_swm_pin(); 33 | 34 | // Configure the LED pins. The API tracks the state of pins at compile time, 35 | // to prevent any mistakes. 36 | let (red, _) = swm.movable_functions.t0_mat0.assign(red, &mut handle); 37 | let (green, _) = swm.movable_functions.t0_mat1.assign(green, &mut handle); 38 | let (blue, _) = swm.movable_functions.t0_mat2.assign(blue, &mut handle); 39 | 40 | // Use 8 bit pwm 41 | let ctimer = p 42 | .CTIMER0 43 | .enable(256, 0, &mut syscon.handle) 44 | .attach(red) 45 | .attach(green) 46 | .attach(blue); 47 | let mut red = ctimer.channels.channel1; 48 | let mut green = ctimer.channels.channel2; 49 | let mut blue = ctimer.channels.channel3; 50 | 51 | // Fade each color after another 52 | loop { 53 | for i in 0..red.get_max_duty() { 54 | delay.delay_ms(4_u8); 55 | red.set_duty(i); 56 | } 57 | for i in (0..red.get_max_duty()).rev() { 58 | delay.delay_ms(4_u8); 59 | red.set_duty(i); 60 | } 61 | for i in 0..green.get_max_duty() { 62 | delay.delay_ms(4_u8); 63 | green.set_duty(i); 64 | } 65 | for i in (0..green.get_max_duty()).rev() { 66 | delay.delay_ms(4_u8); 67 | green.set_duty(i); 68 | } 69 | for i in 0..blue.get_max_duty() { 70 | delay.delay_ms(4_u8); 71 | blue.set_duty(i); 72 | } 73 | for i in (0..blue.get_max_duty()).rev() { 74 | delay.delay_ms(4_u8); 75 | blue.set_duty(i); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /examples/gpio_delay.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m_rt::entry, delay::Delay, gpio::Level, prelude::*, CorePeripherals, 8 | Peripherals, 9 | }; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | rtt_target::rtt_init_print!(); 14 | 15 | // Get access to the device's peripherals. Since only one instance of this 16 | // struct can exist, the call to `take` returns an `Option`. 17 | // If we tried to call the method a second time, it would return `None`, but 18 | // we're only calling it the one time here, so we can safely `unwrap` the 19 | // `Option` without causing a panic. 20 | let cp = CorePeripherals::take().unwrap(); 21 | let p = Peripherals::take().unwrap(); 22 | 23 | // Initialize the APIs of the peripherals we need. 24 | let mut delay = Delay::new(cp.SYST); 25 | #[cfg(feature = "82x")] 26 | let gpio = p.GPIO; // GPIO is initialized by default on LPC82x. 27 | #[cfg(feature = "845")] 28 | let gpio = { 29 | let mut syscon = p.SYSCON.split(); 30 | p.GPIO.enable(&mut syscon.handle) 31 | }; 32 | 33 | // Select pin for LED 34 | #[cfg(feature = "82x")] 35 | let (led, token) = (p.pins.pio0_12, gpio.tokens.pio0_12); 36 | #[cfg(feature = "845")] 37 | let (led, token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 38 | 39 | // Configure the LED pin. The API tracks the state of pins at compile time, 40 | // to prevent any mistakes. 41 | let mut led = led.into_output_pin(token, Level::Low); 42 | 43 | // Blink the LED using the systick with the delay traits 44 | loop { 45 | delay.delay_ms(1_000_u16); 46 | led.set_high(); 47 | delay.delay_ms(1_000_u16); 48 | led.set_low(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/gpio_dynamic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m_rt::entry, gpio::Level, pins::DynamicPinDirection, Peripherals, 8 | }; 9 | 10 | #[entry] 11 | fn main() -> ! { 12 | rtt_target::rtt_init_print!(); 13 | 14 | // Get access to the device's peripherals. Since only one instance of this 15 | // struct can exist, the call to `take` returns an `Option`. 16 | // If we tried to call the method a second time, it would return `None`, but 17 | // we're only calling it the one time here, so we can safely `unwrap` the 18 | // `Option` without causing a panic. 19 | let p = Peripherals::take().unwrap(); 20 | 21 | // Initialize the APIs of the peripherals we need. 22 | #[cfg(feature = "82x")] 23 | let gpio = p.GPIO; // GPIO is initialized by default on LPC82x. 24 | #[cfg(feature = "845")] 25 | let gpio = { 26 | let mut syscon = p.SYSCON.split(); 27 | p.GPIO.enable(&mut syscon.handle) 28 | }; 29 | 30 | // Select pin for LED 31 | #[cfg(feature = "82x")] 32 | let (led, token) = (p.pins.pio0_12, gpio.tokens.pio0_12); 33 | #[cfg(feature = "845")] 34 | let (led, token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 35 | 36 | // Configure the LED pin as dynamic, with its initial direction being Input. 37 | // A dynamic pin can change ist direction at runtime, but will not give you the same 38 | // compile-time guarantees a unidirectinal pin gives you. 39 | let mut led = 40 | led.into_dynamic_pin(token, Level::Low, DynamicPinDirection::Input); 41 | 42 | // Blink the LED by toggling the pin direction 43 | loop { 44 | for _ in 0..10_000 { 45 | led.switch_to_output(Level::Low); 46 | } 47 | for _ in 0..10_000 { 48 | led.switch_to_input(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/gpio_generic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m_rt::entry, 8 | delay::Delay, 9 | gpio::{direction::Dynamic, GpioPin, Level}, 10 | pins::{DynamicPinDirection, GenericPin}, 11 | prelude::*, 12 | CorePeripherals, Peripherals, 13 | }; 14 | 15 | #[entry] 16 | fn main() -> ! { 17 | rtt_target::rtt_init_print!(); 18 | 19 | // Get access to the device's peripherals. Since only one instance of this 20 | // struct can exist, the call to `take` returns an `Option`. 21 | // If we tried to call the method a second time, it would return `None`, but 22 | // we're only calling it the one time here, so we can safely `unwrap` the 23 | // `Option` without causing a panic. 24 | let cp = CorePeripherals::take().unwrap(); 25 | let p = Peripherals::take().unwrap(); 26 | 27 | // Initialize the APIs of the peripherals we need. 28 | let mut delay = Delay::new(cp.SYST); 29 | 30 | let mut syscon = p.SYSCON.split(); 31 | let gpio = p.GPIO.enable(&mut syscon.handle); 32 | 33 | // Select pins for all three LEDs 34 | let (green_led, green_led_token) = (p.pins.pio1_0, gpio.tokens.pio1_0); 35 | let (blue_led, blue_led_token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 36 | let (red_led, red_led_token) = (p.pins.pio1_2, gpio.tokens.pio1_2); 37 | 38 | // Generate Generic Dynamic Pins from `Token` + `Pin`s and gather them for further batch 39 | // processing. 40 | let mut leds: [GpioPin; 3] = [ 41 | green_led.into_generic_dynamic_pin( 42 | green_led_token, 43 | Level::High, // led is off initially 44 | DynamicPinDirection::Output, 45 | ), 46 | blue_led.into_generic_dynamic_pin( 47 | blue_led_token, 48 | Level::High, // led is off initially 49 | DynamicPinDirection::Output, 50 | ), 51 | red_led.into_generic_dynamic_pin( 52 | red_led_token, 53 | Level::High, // led is off initially 54 | DynamicPinDirection::Output, 55 | ), 56 | ]; 57 | 58 | loop { 59 | // Blink all LED colors by looping through the array that holds them 60 | // This should make the on-board LED blink like this: 61 | // 🟢 🔵 🔴 🟢 🔵 🔴 🟢 🔵 🔴 ... 62 | for led in leds.iter_mut() { 63 | blink_led(led, &mut delay); 64 | } 65 | } 66 | } 67 | 68 | /// Turn `led` on for 1000 ms 69 | fn blink_led(led: &mut GpioPin, delay: &mut Delay) { 70 | led.set_low(); 71 | delay.delay_ms(1_000_u16); 72 | led.set_high(); 73 | } 74 | -------------------------------------------------------------------------------- /examples/gpio_input.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{cortex_m_rt::entry, gpio::Level, Peripherals}; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_target::rtt_init_print!(); 11 | 12 | // Get access to the device's peripherals. Since only one instance of this 13 | // struct can exist, the call to `take` returns an `Option`. 14 | // If we tried to call the method a second time, it would return `None`, but 15 | // we're only calling it the one time here, so we can safely `unwrap` the 16 | // `Option` without causing a panic. 17 | let p = Peripherals::take().unwrap(); 18 | 19 | // Initialize the APIs of the peripherals we need. 20 | let gpio = { 21 | let mut syscon = p.SYSCON.split(); 22 | p.GPIO.enable(&mut syscon.handle) 23 | }; 24 | 25 | // Select pin for LED 26 | let led = p.pins.pio1_1; 27 | 28 | // Select pin for button 29 | let button = p.pins.pio0_4; 30 | 31 | // Configure the LED pin. The API tracks the state of pins at compile time, 32 | // to prevent any mistakes. 33 | let mut led = led.into_output_pin(gpio.tokens.pio1_1, Level::Low); 34 | 35 | // Configure the button pin. The API tracks the state of pins at compile time, 36 | // to prevent any mistakes. 37 | let button = button.into_input_pin(gpio.tokens.pio0_4); 38 | 39 | // Display the state of the button on the led 40 | loop { 41 | // If the button is high (not pressed) 42 | if button.is_high() { 43 | // Disable the LED 44 | led.set_high(); 45 | } else { 46 | // Otherwise, enable it 47 | led.set_low(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/gpio_simple.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{cortex_m_rt::entry, gpio::Level, Peripherals}; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_target::rtt_init_print!(); 11 | 12 | // Get access to the device's peripherals. Since only one instance of this 13 | // struct can exist, the call to `take` returns an `Option`. 14 | // If we tried to call the method a second time, it would return `None`, but 15 | // we're only calling it the one time here, so we can safely `unwrap` the 16 | // `Option` without causing a panic. 17 | let p = Peripherals::take().unwrap(); 18 | 19 | // Initialize the APIs of the peripherals we need. 20 | #[cfg(feature = "82x")] 21 | let gpio = p.GPIO; // GPIO is initialized by default on LPC82x. 22 | #[cfg(feature = "845")] 23 | let gpio = { 24 | let mut syscon = p.SYSCON.split(); 25 | p.GPIO.enable(&mut syscon.handle) 26 | }; 27 | 28 | // Select pin for LED 29 | #[cfg(feature = "82x")] 30 | let (led, token) = (p.pins.pio0_12, gpio.tokens.pio0_12); 31 | #[cfg(feature = "845")] 32 | let (led, token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 33 | 34 | // Configure the LED pin. The API tracks the state of pins at compile time, 35 | // to prevent any mistakes. 36 | let mut led = led.into_output_pin(token, Level::Low); 37 | 38 | // Blink the LED 39 | // 40 | // For this simple demo accurate timing isn't required and this is the 41 | // simplest method to delay. The values are chosen to give a nice blinking 42 | // pattern in release mode. 43 | loop { 44 | for _ in 0..1_000_000 { 45 | led.set_high(); 46 | } 47 | for _ in 0..100_000 { 48 | led.set_low(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/gpio_sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | clock::Ticks, cortex_m_rt::entry, gpio::Level, prelude::*, sleep, 8 | Peripherals, 9 | }; 10 | 11 | #[entry] 12 | fn main() -> ! { 13 | rtt_target::rtt_init_print!(); 14 | 15 | // Get access to the device's peripherals. Since only one instance of this 16 | // struct can exist, the call to `take` returns an `Option`. 17 | // If we tried to call the method a second time, it would return `None`, but 18 | // we're only calling it the one time here, so we can safely `unwrap` the 19 | // `Option` without causing a panic. 20 | let p = Peripherals::take().unwrap(); 21 | 22 | // Initialize the APIs of the peripherals we need. 23 | let mut syscon = p.SYSCON.split(); 24 | let mut wkt = p.WKT.enable(&mut syscon.handle); 25 | #[cfg(feature = "82x")] 26 | let gpio = p.GPIO; // GPIO is initialized by default on LPC82x. 27 | #[cfg(feature = "845")] 28 | let gpio = p.GPIO.enable(&mut syscon.handle); 29 | 30 | // We're going to need a clock for sleeping. Let's use the internal oscillator/IRC/FRO-derived clock 31 | // that runs at 750 kHz. 32 | let clock = syscon.iosc_derived_clock; 33 | 34 | // Select pin for LED 35 | #[cfg(feature = "82x")] 36 | let (led, token) = (p.pins.pio0_12, gpio.tokens.pio0_12); 37 | #[cfg(feature = "845")] 38 | let (led, token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 39 | 40 | // Configure the LED pin. The API tracks the state of pins at compile time, 41 | // to prevent any mistakes. 42 | let mut led = led.into_output_pin(token, Level::Low); 43 | 44 | // Let's already initialize the durations that we're going to sleep for 45 | // between changing the LED state. We do this by specifying the number of 46 | // clock ticks directly, but a real program could use a library that allows 47 | // us to specify the time in milliseconds. 48 | // Each duration also keeps a reference to the clock, as to prevent other 49 | // parts of the program from accidentally disabling the clock, or changing 50 | // its settings. 51 | let low_time = Ticks { 52 | value: 37_500, 53 | clock: &clock, 54 | }; // 50 ms 55 | let high_time = Ticks { 56 | value: 712_500, 57 | clock: &clock, 58 | }; // 950 ms 59 | 60 | // Since this is a simple example, we don't want to deal with interrupts 61 | // here. Let's just use busy waiting as a sleeping strategy. 62 | let mut sleep = sleep::Busy::prepare(&mut wkt); 63 | 64 | // Blink the LED 65 | loop { 66 | led.set_high(); 67 | sleep.sleep(high_time); 68 | led.set_low(); 69 | sleep.sleep(low_time); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/gpio_timer.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use core::convert::TryInto as _; 7 | 8 | use lpc8xx_hal::{ 9 | cortex_m_rt::entry, embedded_time::duration::Extensions as _, gpio::Level, 10 | prelude::*, Peripherals, 11 | }; 12 | 13 | use nb::block; 14 | #[entry] 15 | fn main() -> ! { 16 | rtt_target::rtt_init_print!(); 17 | 18 | // Get access to the device's peripherals. Since only one instance of this 19 | // struct can exist, the call to `take` returns an `Option`. 20 | // If we tried to call the method a second time, it would return `None`, but 21 | // we're only calling it the one time here, so we can safely `unwrap` the 22 | // `Option` without causing a panic. 23 | let p = Peripherals::take().unwrap(); 24 | 25 | // Initialize the APIs of the peripherals we need. 26 | let mut syscon = p.SYSCON.split(); 27 | let mrt_channels = p.MRT0.split(&mut syscon.handle); 28 | let mut timer = mrt_channels.mrt0; 29 | 30 | #[cfg(feature = "82x")] 31 | let gpio = p.GPIO; // GPIO is initialized by default on LPC82x. 32 | #[cfg(feature = "845")] 33 | let gpio = p.GPIO.enable(&mut syscon.handle); 34 | 35 | // Select pin for LED 36 | #[cfg(feature = "82x")] 37 | let (led, token) = (p.pins.pio0_12, gpio.tokens.pio0_12); 38 | #[cfg(feature = "845")] 39 | let (led, token) = (p.pins.pio1_1, gpio.tokens.pio1_1); 40 | 41 | // Configure the LED pin. The API tracks the state of pins at compile time, 42 | // to prevent any mistakes. 43 | let mut led = led.into_output_pin(token, Level::Low); 44 | 45 | // Start the timer with an interval of one second 46 | timer.start(1.seconds().try_into().unwrap()); 47 | 48 | // Blink the LED using the systick with the delay traits 49 | loop { 50 | block!(timer.wait()).unwrap(); 51 | led.set_high(); 52 | block!(timer.wait()).unwrap(); 53 | led.set_low(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/i2c_eeprom.rs: -------------------------------------------------------------------------------- 1 | //! I2C example using an 256 byte eeprom 2 | //! 3 | //! This example expects the microcontroller to be connected to the eeprom in 4 | //! the following way: 5 | //! - PIO0_11/I2C0_SDA to SDA 6 | //! - PIO0_10/I2C0_SCL to SCL 7 | //! - VSS to GND 8 | //! - VDD to VDD 9 | 10 | #![no_main] 11 | #![no_std] 12 | 13 | extern crate panic_rtt_target; 14 | 15 | use core::fmt::Write; 16 | 17 | use lpc8xx_hal::{ 18 | cortex_m_rt::entry, delay::Delay, i2c, prelude::*, usart, CorePeripherals, 19 | Peripherals, 20 | }; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | rtt_target::rtt_init_print!(); 25 | 26 | let cp = CorePeripherals::take().unwrap(); 27 | let p = Peripherals::take().unwrap(); 28 | 29 | let mut delay = Delay::new(cp.SYST); 30 | let i2c = p.I2C0; 31 | let swm = p.SWM.split(); 32 | let mut syscon = p.SYSCON.split(); 33 | 34 | #[cfg(feature = "82x")] 35 | let mut handle = swm.handle; 36 | #[cfg(feature = "845")] 37 | let mut handle = swm.handle.enable(&mut syscon.handle); // SWM isn't enabled by default on LPC845. 38 | 39 | #[cfg(feature = "82x")] 40 | // Set baud rate to 115200 baud 41 | // 42 | // See the usart example for a detailed explanation on how the usart setup works 43 | let clock_config = { 44 | syscon.uartfrg.set_clkdiv(6); 45 | syscon.uartfrg.set_frgmult(22); 46 | syscon.uartfrg.set_frgdiv(0xff); 47 | usart::Clock::new(&syscon.uartfrg, 0, 16) 48 | }; 49 | #[cfg(feature = "845")] 50 | // Set baud rate to 115200 baud 51 | let clock_config = usart::Clock::new_with_baudrate(115200); 52 | #[cfg(feature = "82x")] 53 | let tx_pin = p.pins.pio0_7.into_swm_pin(); 54 | #[cfg(feature = "82x")] 55 | let rx_pin = p.pins.pio0_18.into_swm_pin(); 56 | #[cfg(feature = "845")] 57 | let tx_pin = p.pins.pio0_25.into_swm_pin(); 58 | #[cfg(feature = "845")] 59 | let rx_pin = p.pins.pio0_24.into_swm_pin(); 60 | 61 | let (u0_rxd, _) = swm.movable_functions.u0_rxd.assign(rx_pin, &mut handle); 62 | let (u0_txd, _) = swm.movable_functions.u0_txd.assign(tx_pin, &mut handle); 63 | 64 | let mut serial = p.USART0.enable_async( 65 | &clock_config, 66 | &mut syscon.handle, 67 | u0_rxd, 68 | u0_txd, 69 | usart::Settings::default(), 70 | ); 71 | 72 | serial 73 | .bwrite_all(b"Initializing I2C...\n") 74 | .expect("Write should never fail"); 75 | 76 | let (i2c0_sda, _) = swm 77 | .fixed_functions 78 | .i2c0_sda 79 | .assign(p.pins.pio0_11.into_swm_pin(), &mut handle); 80 | let (i2c0_scl, _) = swm 81 | .fixed_functions 82 | .i2c0_scl 83 | .assign(p.pins.pio0_10.into_swm_pin(), &mut handle); 84 | 85 | #[cfg(feature = "82x")] 86 | let i2c_clock = &(); 87 | #[cfg(feature = "845")] 88 | let i2c_clock = &syscon.iosc; 89 | 90 | let mut i2c = i2c 91 | .enable(i2c_clock, i2c0_scl, i2c0_sda, &mut syscon.handle) 92 | .enable_master_mode(&i2c::Clock::new_400khz()); 93 | 94 | // Address of the eeprom 95 | // ADJUST THIS 96 | let address = 0b101_0000; 97 | 98 | serial 99 | .bwrite_all(b"Writing data...\n") 100 | .expect("Write should never fail"); 101 | 102 | // Write an 'Hi' to address 0 & 1 103 | i2c.master 104 | .write(address, &[0, b'H', b'i']) 105 | .expect("Failed to write data"); 106 | 107 | serial 108 | .bwrite_all(b"Reading data...\n") 109 | .expect("Write should never fail"); 110 | 111 | // Wait a bit until the write has gone through 112 | delay.delay_ms(1_000_u16); 113 | 114 | // Read value from the eeprom 115 | let mut buffer = [0u8; 2]; 116 | // Set the address to 0 again 117 | i2c.master 118 | .write(address, &[0]) 119 | .expect("Failed to write data"); 120 | // Read the two bytes at 0 & 1 121 | i2c.master 122 | .read(address, &mut buffer) 123 | .expect("Failed to read data"); 124 | 125 | write!(serial, "{:?}\n", &buffer).expect("Write should never fail"); 126 | 127 | // Check if they're correct 128 | if buffer == *b"Hi" { 129 | serial 130 | .bwrite_all(b"SUCCESS!\n") 131 | .expect("Write should never fail"); 132 | } else { 133 | serial 134 | .bwrite_all(b"FAILURE!\n") 135 | .expect("Write should never fail"); 136 | } 137 | 138 | loop {} 139 | } 140 | -------------------------------------------------------------------------------- /examples/i2c_master_slave.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | #[rtic::app(device = lpc8xx_hal::pac, peripherals = false)] 7 | mod app { 8 | use core::marker::PhantomData; 9 | 10 | use lpc8xx_hal::{ 11 | i2c, init_state::Enabled, pac::I2C0, prelude::*, syscon::IOSC, 12 | Peripherals, 13 | }; 14 | use rtt_target::rprintln; 15 | 16 | const ADDRESS: u8 = 0x24; 17 | 18 | #[shared] 19 | struct Shared {} 20 | 21 | #[local] 22 | struct Local { 23 | i2c_master: i2c::Master>, Enabled>, 24 | i2c_slave: i2c::Slave>, Enabled>, 25 | i2c_data: Option, 26 | } 27 | 28 | #[init] 29 | fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { 30 | rtt_target::rtt_init_print!(); 31 | 32 | let p = Peripherals::take().unwrap(); 33 | 34 | let mut syscon = p.SYSCON.split(); 35 | let swm = p.SWM.split(); 36 | 37 | let mut swm_handle = swm.handle.enable(&mut syscon.handle); 38 | 39 | let (i2c0_scl, _) = swm 40 | .fixed_functions 41 | .i2c0_scl 42 | .assign(p.pins.pio0_10.into_swm_pin(), &mut swm_handle); 43 | let (i2c0_sda, _) = swm 44 | .fixed_functions 45 | .i2c0_sda 46 | .assign(p.pins.pio0_11.into_swm_pin(), &mut swm_handle); 47 | 48 | let mut i2c = p 49 | .I2C0 50 | .enable(&syscon.iosc, i2c0_scl, i2c0_sda, &mut syscon.handle) 51 | .enable_master_mode(&i2c::Clock::new_400khz()) 52 | .enable_slave_mode(ADDRESS) 53 | .expect("`ADDRESS` not a valid 7-bit address"); 54 | 55 | i2c.enable_interrupts(i2c::Interrupts { 56 | slave_pending: true, 57 | ..Default::default() 58 | }); 59 | 60 | ( 61 | Shared {}, 62 | Local { 63 | i2c_master: i2c.master, 64 | i2c_slave: i2c.slave, 65 | i2c_data: None, 66 | }, 67 | init::Monotonics(), 68 | ) 69 | } 70 | 71 | #[idle(local = [i2c_master])] 72 | fn idle(context: idle::Context) -> ! { 73 | let data = 0x14; 74 | 75 | let i2c = context.local.i2c_master; 76 | 77 | loop { 78 | rprintln!("MASTER: Starting I2C transaction..."); 79 | 80 | // Write data to slave 81 | i2c.write(ADDRESS, &[data]).unwrap(); 82 | 83 | rprintln!("MASTER: Data written."); 84 | 85 | // Read data from slave 86 | let mut reply = [0; 1]; 87 | i2c.read(ADDRESS, &mut reply).unwrap(); 88 | 89 | rprintln!("MASTER: Reply read."); 90 | 91 | // Verify that slave replied with the correct data 92 | assert_eq!(reply[0], data * 2); 93 | 94 | rprintln!("MASTER: Reply verified."); 95 | } 96 | } 97 | 98 | #[task(binds = I2C0, local = [i2c_slave, i2c_data])] 99 | fn i2c0(context: i2c0::Context) { 100 | let i2c = context.local.i2c_slave; 101 | let data = context.local.i2c_data; 102 | 103 | rprintln!("SLAVE: Handling interrupt..."); 104 | 105 | match i2c.wait() { 106 | Ok(i2c::slave::State::AddressMatched(i2c)) => { 107 | rprintln!("SLAVE: Address matched."); 108 | 109 | i2c.ack().unwrap(); 110 | 111 | rprintln!("SLAVE: Ack'ed address."); 112 | } 113 | Ok(i2c::slave::State::RxReady(i2c)) => { 114 | rprintln!("SLAVE: Ready to receive."); 115 | 116 | *data = Some(i2c.read().unwrap()); 117 | i2c.ack().unwrap(); 118 | 119 | rprintln!("SLAVE: Received and ack'ed."); 120 | } 121 | Ok(i2c::slave::State::TxReady(i2c)) => { 122 | rprintln!("SLAVE: Ready to transmit."); 123 | 124 | if let Some(data) = *data { 125 | i2c.transmit(data << 1).unwrap(); 126 | rprintln!("SLAVE: Transmitted."); 127 | } 128 | } 129 | Err(nb::Error::WouldBlock) => { 130 | // I2C not ready; nothing to do 131 | } 132 | Err(err) => { 133 | panic!("Error: {:?}", err); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/i2c_master_slave_dma.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | #[rtic::app(device = lpc8xx_hal::pac, peripherals = false)] 7 | mod app { 8 | use core::marker::PhantomData; 9 | 10 | use lpc8xx_hal::{ 11 | dma, i2c, init_state::Enabled, pac::I2C0, syscon::IOSC, Peripherals, 12 | }; 13 | use rtt_target::rprintln; 14 | 15 | const ADDRESS: u8 = 0x24; 16 | 17 | #[shared] 18 | struct Shared {} 19 | 20 | #[local] 21 | struct Local { 22 | i2c_master: 23 | Option>, Enabled>>, 24 | i2c_slave: i2c::Slave>, Enabled>, 25 | i2c_data: Option, 26 | 27 | dma_channel: 28 | Option::MstChannel, Enabled>>, 29 | dma_rx_buf: [u8; 1], 30 | } 31 | 32 | #[init] 33 | fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { 34 | rtt_target::rtt_init_print!(); 35 | 36 | let p = Peripherals::take().unwrap(); 37 | 38 | let mut syscon = p.SYSCON.split(); 39 | let swm = p.SWM.split(); 40 | let dma = p.DMA.enable(&mut syscon.handle); 41 | 42 | let mut swm_handle = swm.handle.enable(&mut syscon.handle); 43 | 44 | let (i2c0_scl, _) = swm 45 | .fixed_functions 46 | .i2c0_scl 47 | .assign(p.pins.pio0_10.into_swm_pin(), &mut swm_handle); 48 | let (i2c0_sda, _) = swm 49 | .fixed_functions 50 | .i2c0_sda 51 | .assign(p.pins.pio0_11.into_swm_pin(), &mut swm_handle); 52 | 53 | let mut i2c = p 54 | .I2C0 55 | .enable(&syscon.iosc, i2c0_scl, i2c0_sda, &mut syscon.handle) 56 | .enable_master_mode(&i2c::Clock::new_400khz()) 57 | .enable_slave_mode(ADDRESS) 58 | .expect("`ADDRESS` not a valid 7-bit address"); 59 | 60 | i2c.enable_interrupts(i2c::Interrupts { 61 | slave_pending: true, 62 | ..Default::default() 63 | }); 64 | 65 | ( 66 | Shared {}, 67 | Local { 68 | i2c_master: Some(i2c.master), 69 | i2c_slave: i2c.slave, 70 | i2c_data: None, 71 | dma_channel: Some(dma.channels.channel15), 72 | dma_rx_buf: [0; 1], 73 | }, 74 | init::Monotonics(), 75 | ) 76 | } 77 | 78 | #[idle(local = [i2c_master, dma_channel, dma_rx_buf])] 79 | fn idle(context: idle::Context) -> ! { 80 | static TX_BUF: [u8; 1] = [0x14]; 81 | 82 | // The `.take().unwrap()` workaround is required, because RTIC won't 83 | // allow us to move resources in here directly. 84 | let mut i2c = context.local.i2c_master.take().unwrap(); 85 | let mut channel = context.local.dma_channel.take().unwrap(); 86 | let mut rx_buf = &mut context.local.dma_rx_buf[..]; 87 | 88 | loop { 89 | rprintln!("MASTER: Starting I2C transaction..."); 90 | 91 | // Write data to slave 92 | let payload = i2c 93 | .write_all(ADDRESS, &TX_BUF[..], channel) 94 | .unwrap() 95 | .start() 96 | .wait() 97 | .unwrap(); 98 | 99 | channel = payload.channel; 100 | i2c = payload.dest; 101 | 102 | rprintln!("MASTER: Data written."); 103 | 104 | // Read data from slave 105 | rx_buf[0] = 0; 106 | let payload = i2c 107 | .read_all(ADDRESS, rx_buf, channel) 108 | .unwrap() 109 | .start() 110 | .wait() 111 | .unwrap(); 112 | 113 | channel = payload.channel; 114 | i2c = payload.source; 115 | rx_buf = payload.dest; 116 | 117 | rprintln!("MASTER: Reply read."); 118 | 119 | // Verify that slave replied with the correct data 120 | assert_eq!(rx_buf[0], TX_BUF[0] * 2); 121 | 122 | rprintln!("MASTER: Reply verified."); 123 | } 124 | } 125 | 126 | #[task(binds = I2C0, local = [i2c_slave, i2c_data])] 127 | fn i2c0(context: i2c0::Context) { 128 | let i2c = context.local.i2c_slave; 129 | let data = context.local.i2c_data; 130 | 131 | rprintln!("SLAVE: Handling interrupt..."); 132 | 133 | match i2c.wait() { 134 | Ok(i2c::slave::State::AddressMatched(i2c)) => { 135 | rprintln!("SLAVE: Address matched."); 136 | 137 | i2c.ack().unwrap(); 138 | 139 | rprintln!("SLAVE: Ack'ed address."); 140 | } 141 | Ok(i2c::slave::State::RxReady(i2c)) => { 142 | rprintln!("SLAVE: Ready to receive."); 143 | 144 | *data = Some(i2c.read().unwrap()); 145 | i2c.ack().unwrap(); 146 | 147 | rprintln!("SLAVE: Received and ack'ed."); 148 | } 149 | Ok(i2c::slave::State::TxReady(i2c)) => { 150 | rprintln!("SLAVE: Ready to transmit."); 151 | 152 | if let Some(data) = *data { 153 | i2c.transmit(data << 1).unwrap(); 154 | rprintln!("SLAVE: Transmitted."); 155 | } 156 | } 157 | Err(nb::Error::WouldBlock) => { 158 | // I2C not ready; nothing to do 159 | } 160 | Err(err) => { 161 | panic!("Error: {:?}", err); 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /examples/i2c_vl53l0x.rs: -------------------------------------------------------------------------------- 1 | //! I2C example using the VL53L0X distance sensor by ST 2 | //! 3 | //! This example expects the microcontroller to be connected to a VL53L0X 4 | //! satellite board in the following way: 5 | //! - PIO0_11/I2C0_SDA to SDA_I 6 | //! - PIO0_10/I2C0_SCL to SCL_I 7 | //! - VSS to GND 8 | //! - VDD to VDD 9 | //! - VDD to XSDN_I, via a 10 kOhm pull-up resistor 10 | 11 | #![no_main] 12 | #![no_std] 13 | 14 | extern crate panic_rtt_target; 15 | 16 | use core::fmt::Write; 17 | 18 | use lpc8xx_hal::{cortex_m_rt::entry, i2c, prelude::*, usart, Peripherals}; 19 | 20 | const ADDRESS: u8 = 0x29; 21 | 22 | #[entry] 23 | fn main() -> ! { 24 | rtt_target::rtt_init_print!(); 25 | 26 | let p = Peripherals::take().unwrap(); 27 | 28 | let i2c = p.I2C0; 29 | let mut swm = p.SWM.split(); 30 | let mut syscon = p.SYSCON.split(); 31 | 32 | // Set baud rate to 115200 baud 33 | // 34 | // The common peripheral clock for all UART units, U_PCLK, needs to be set 35 | // to 16 times the desired baud rate. This results in a frequency of 36 | // 1843200 Hz for U_PLCK. 37 | // 38 | // We assume the main clock runs at 12 Mhz. To get close to the desired 39 | // frequency for U_PLCK, we divide that by 6 using UARTCLKDIV, resulting in 40 | // a frequency of 2 Mhz. 41 | // 42 | // To get to the desired 1843200 Hz, we need to further divide the frequency 43 | // using the fractional baud rate generator. The fractional baud rate 44 | // generator divides the frequency by `1 + MULT/DIV`. 45 | // 46 | // DIV must always be 256. To achieve this, we need to set the UARTFRGDIV to 47 | // 0xff. MULT can then be fine-tuned to get as close as possible to the 48 | // desired value. We choose the value 22, which we write into UARTFRGMULT. 49 | // 50 | // Finally, we can set an additional divider value for the UART unit by 51 | // writing to the BRG register. As we are already close enough to the 52 | // desired value, we write 0, resulting in no further division. 53 | // 54 | // All of this is somewhat explained in the user manual, section 13.3.1. 55 | syscon.uartfrg.set_clkdiv(6); 56 | syscon.uartfrg.set_frgmult(22); 57 | syscon.uartfrg.set_frgdiv(0xff); 58 | 59 | let (u0_rxd, _) = swm 60 | .movable_functions 61 | .u0_rxd 62 | .assign(p.pins.pio0_0.into_swm_pin(), &mut swm.handle); 63 | let (u0_txd, _) = swm 64 | .movable_functions 65 | .u0_txd 66 | .assign(p.pins.pio0_4.into_swm_pin(), &mut swm.handle); 67 | 68 | let mut serial = p.USART0.enable_async( 69 | &usart::Clock::new(&syscon.uartfrg, 0, 16), 70 | &mut syscon.handle, 71 | u0_rxd, 72 | u0_txd, 73 | usart::Settings::default(), 74 | ); 75 | 76 | serial 77 | .bwrite_all(b"Initializing I2C...\n") 78 | .expect("Write should never fail"); 79 | 80 | let (i2c0_sda, _) = swm 81 | .fixed_functions 82 | .i2c0_sda 83 | .assign(p.pins.pio0_11.into_swm_pin(), &mut swm.handle); 84 | let (i2c0_scl, _) = swm 85 | .fixed_functions 86 | .i2c0_scl 87 | .assign(p.pins.pio0_10.into_swm_pin(), &mut swm.handle); 88 | 89 | let i2c_clock = i2c::Clock::new_400khz(); 90 | let mut i2c = i2c 91 | .enable(&(), i2c0_scl, i2c0_sda, &mut syscon.handle) 92 | .enable_master_mode(&i2c_clock); 93 | 94 | serial 95 | .bwrite_all(b"Writing data...\n") 96 | .expect("Write should never fail"); 97 | 98 | // Write index of reference register 99 | i2c.master 100 | .write(ADDRESS, &[0xC0]) 101 | .expect("Failed to write data"); 102 | 103 | serial 104 | .bwrite_all(b"Receiving data...\n") 105 | .expect("Write should never fail"); 106 | 107 | // Read value from reference register 108 | let mut buffer = [0u8; 1]; 109 | i2c.master 110 | .read(ADDRESS, &mut buffer) 111 | .expect("Failed to read data"); 112 | 113 | write!(serial, "{:#X}\n", buffer[0]).expect("Write should never fail"); 114 | 115 | if buffer[0] == 0xEE { 116 | serial 117 | .bwrite_all(b"SUCCESS!\n") 118 | .expect("Write should never fail"); 119 | } else { 120 | serial 121 | .bwrite_all(b"FAILURE!\n") 122 | .expect("Write should never fail"); 123 | } 124 | 125 | loop {} 126 | } 127 | -------------------------------------------------------------------------------- /examples/mrt_clock.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use embedded_time::{duration::Extensions as _, Clock as _}; 7 | use lpc8xx_hal::{cortex_m_rt::entry, gpio::Level, mrt, Peripherals}; 8 | 9 | #[entry] 10 | fn main() -> ! { 11 | rtt_target::rtt_init_print!(); 12 | 13 | let p = Peripherals::take().unwrap(); 14 | 15 | let mut syscon = p.SYSCON.split(); 16 | let gpio = p.GPIO.enable(&mut syscon.handle); 17 | let mut mrt = p.MRT0.split(&mut syscon.handle).mrt0; 18 | 19 | let mut led = p 20 | .pins 21 | .pio1_1 22 | .into_output_pin(gpio.tokens.pio1_1, Level::Low); 23 | 24 | loop { 25 | mrt.start(mrt::MAX_VALUE); 26 | 27 | let timer = mrt.new_timer(1u32.seconds()); 28 | timer.start().unwrap().wait().unwrap(); 29 | 30 | led.toggle(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/pinint.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | #[rtic::app(device = lpc8xx_hal::pac, peripherals = false)] 7 | mod app { 8 | use lpc8xx_hal::{ 9 | gpio::{direction::Output, GpioPin, Level}, 10 | init_state::Enabled, 11 | pinint::{self, PININT0}, 12 | pins::{PIO0_4, PIO1_1}, 13 | Peripherals, 14 | }; 15 | 16 | #[shared] 17 | struct Shared {} 18 | 19 | #[local] 20 | struct Local { 21 | int: pinint::Interrupt, 22 | led: GpioPin, 23 | } 24 | 25 | #[init] 26 | fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { 27 | rtt_target::rtt_init_print!(); 28 | 29 | let p = Peripherals::take().unwrap(); 30 | 31 | let mut syscon = p.SYSCON.split(); 32 | let gpio = p.GPIO.enable(&mut syscon.handle); 33 | let pinint = p.PININT.enable(&mut syscon.handle); 34 | 35 | let button = p.pins.pio0_4.into_input_pin(gpio.tokens.pio0_4); 36 | let mut int = pinint 37 | .interrupts 38 | .pinint0 39 | .select::(button.inner(), &mut syscon.handle); 40 | int.enable_rising_edge(); 41 | int.enable_falling_edge(); 42 | 43 | let led = p 44 | .pins 45 | .pio1_1 46 | .into_output_pin(gpio.tokens.pio1_1, Level::High); 47 | 48 | (Shared {}, Local { int, led }, init::Monotonics()) 49 | } 50 | 51 | #[idle] 52 | fn idle(_: idle::Context) -> ! { 53 | // We need an explicit idle loop. Otherwise RTIC inserts a `wfi`, which 54 | // messes with the LPC845's debugging ability, and thus RTT. 55 | loop { 56 | lpc8xx_hal::cortex_m::asm::nop(); 57 | } 58 | } 59 | 60 | #[task(binds = PIN_INT0, local = [int, led])] 61 | fn pinint0(context: pinint0::Context) { 62 | let int = context.local.int; 63 | let led = context.local.led; 64 | 65 | led.toggle(); 66 | 67 | int.clear_rising_edge_flag(); 68 | int.clear_falling_edge_flag(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/pmu.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{ 7 | cortex_m::interrupt, 8 | cortex_m_rt::entry, 9 | nb::block, 10 | pac::{Interrupt, NVIC}, 11 | pmu::LowPowerClock, 12 | prelude::*, 13 | syscon::WktWakeup, 14 | usart, CorePeripherals, Peripherals, 15 | }; 16 | 17 | #[entry] 18 | fn main() -> ! { 19 | rtt_target::rtt_init_print!(); 20 | 21 | let cp = CorePeripherals::take().unwrap(); 22 | let p = Peripherals::take().unwrap(); 23 | 24 | let mut pmu = p.PMU.split(); 25 | let mut swm = p.SWM.split(); 26 | let mut syscon = p.SYSCON.split(); 27 | 28 | // 115200 baud 29 | syscon.uartfrg.set_clkdiv(6); 30 | syscon.uartfrg.set_frgmult(22); 31 | syscon.uartfrg.set_frgdiv(0xff); 32 | let clock_config = usart::Clock::new(&syscon.uartfrg, 0, 16); 33 | 34 | let (u0_rxd, _) = swm 35 | .movable_functions 36 | .u0_rxd 37 | .assign(p.pins.pio0_0.into_swm_pin(), &mut swm.handle); 38 | let (u0_txd, _) = swm 39 | .movable_functions 40 | .u0_txd 41 | .assign(p.pins.pio0_4.into_swm_pin(), &mut swm.handle); 42 | 43 | let mut serial = p.USART0.enable_async( 44 | &clock_config, 45 | &mut syscon.handle, 46 | u0_rxd, 47 | u0_txd, 48 | usart::Settings::default(), 49 | ); 50 | 51 | let _ = pmu.low_power_clock.enable(&mut pmu.handle); 52 | 53 | let mut wkt = p.WKT.enable(&mut syscon.handle); 54 | wkt.select_clock::(); 55 | 56 | let five_seconds: u32 = 10_000 * 5; 57 | 58 | // Need to re-assign some stuff that's needed inside the closure. Otherwise 59 | // it will try to move stuff that's still borrowed outside of it. 60 | let mut pmu = pmu.handle; 61 | let mut scb = cp.SCB; 62 | let mut syscon = syscon.handle; 63 | 64 | interrupt::free(|_| { 65 | // Enable the interrupt for the self-wake-up timer. Doing this within 66 | // `interrupt::free` will allow the interrupt to wake up the system, if 67 | // it's sleeping. But the interrupt handler won't run, which means we 68 | // don't have to define one. 69 | // 70 | // This is safe, as this won't interfere with the critical section. 71 | unsafe { NVIC::unmask(Interrupt::WKT) } 72 | 73 | // Busy Waiting 74 | serial 75 | .bwrite_all(b"5 seconds of busy waiting...\n") 76 | .expect("UART write shouldn't fail"); 77 | wkt.start(five_seconds); 78 | while let Err(nb::Error::WouldBlock) = wkt.wait() {} 79 | 80 | // The timer has finished running and the counter is at zero. Therefore 81 | // the interrupt is currently pending. If we don't do anything about 82 | // this, it will stay pending and will interfere with the following 83 | // calls to `wait`. 84 | // This means we need to clear the interrupt. To prevent it from 85 | // becoming pending again right away, we always do this _after_ starting 86 | // the timer from here on out. 87 | 88 | // Sleep mode 89 | serial 90 | .bwrite_all(b"5 seconds of sleep mode...\n") 91 | .expect("UART write shouldn't fail"); 92 | wkt.start(five_seconds); 93 | NVIC::unpend(Interrupt::WKT); 94 | while let Err(nb::Error::WouldBlock) = wkt.wait() { 95 | pmu.enter_sleep_mode(&mut scb); 96 | } 97 | 98 | // Without this, the WKT interrupt won't wake up the system from 99 | // deep-sleep and power-down modes. 100 | syscon.enable_interrupt_wakeup::(); 101 | 102 | // Deep-sleep mode 103 | serial 104 | .bwrite_all(b"5 seconds of deep-sleep mode...\n") 105 | .expect("UART write shouldn't fail"); 106 | block!(serial.flush()).expect("Flush shouldn't fail"); 107 | wkt.start(five_seconds); 108 | NVIC::unpend(Interrupt::WKT); 109 | while let Err(nb::Error::WouldBlock) = wkt.wait() { 110 | unsafe { pmu.enter_deep_sleep_mode(&mut scb) }; 111 | } 112 | 113 | // Power-down mode 114 | serial 115 | .bwrite_all(b"5 seconds of power-down mode...\n") 116 | .expect("UART write shouldn't fail"); 117 | block!(serial.flush()).expect("Flush shouldn't fail"); 118 | wkt.start(five_seconds); 119 | NVIC::unpend(Interrupt::WKT); 120 | while let Err(nb::Error::WouldBlock) = wkt.wait() { 121 | unsafe { pmu.enter_power_down_mode(&mut scb) }; 122 | } 123 | 124 | // A demonstration of deep power-down mode is currently missing from 125 | // this example, due to some problems with my setup that prevent me from 126 | // testing it for the time being. 127 | 128 | serial 129 | .bwrite_all(b"Done\n") 130 | .expect("UART write shouldn't fail"); 131 | 132 | loop {} 133 | }) 134 | } 135 | -------------------------------------------------------------------------------- /examples/rtic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | #[rtic::app(device = lpc8xx_hal::pac, peripherals = false)] 7 | mod app { 8 | use lpc8xx_hal::{ 9 | delay::Delay, 10 | gpio::{direction::Output, GpioPin, Level}, 11 | pins::PIO1_1, 12 | prelude::*, 13 | Peripherals, 14 | }; 15 | 16 | #[shared] 17 | struct Shared {} 18 | 19 | #[local] 20 | struct Local { 21 | delay: Delay, 22 | led: GpioPin, 23 | } 24 | 25 | #[init] 26 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { 27 | rtt_target::rtt_init_print!(); 28 | 29 | let p = Peripherals::take().unwrap(); 30 | 31 | let delay = Delay::new(cx.core.SYST); 32 | 33 | let mut syscon = p.SYSCON.split(); 34 | let gpio = p.GPIO.enable(&mut syscon.handle); 35 | 36 | let led = p 37 | .pins 38 | .pio1_1 39 | .into_output_pin(gpio.tokens.pio1_1, Level::Low); 40 | 41 | (Shared {}, Local { delay, led }, init::Monotonics()) 42 | } 43 | 44 | #[idle(local = [delay, led])] 45 | fn idle(cx: idle::Context) -> ! { 46 | let delay = cx.local.delay; 47 | let led = cx.local.led; 48 | 49 | loop { 50 | led.set_high(); 51 | delay.delay_ms(700_u16); 52 | led.set_low(); 53 | delay.delay_ms(50_u16); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/spi_apa102.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{cortex_m_rt::entry, prelude::*, spi, Peripherals}; 7 | 8 | use embedded_hal::spi::{Mode, Phase, Polarity}; 9 | 10 | #[entry] 11 | fn main() -> ! { 12 | rtt_target::rtt_init_print!(); 13 | 14 | const MODE: Mode = Mode { 15 | polarity: Polarity::IdleHigh, 16 | phase: Phase::CaptureOnSecondTransition, 17 | }; 18 | let p = Peripherals::take().unwrap(); 19 | 20 | let swm = p.SWM.split(); 21 | let mut syscon = p.SYSCON.split(); 22 | 23 | #[cfg(feature = "82x")] 24 | let mut handle = swm.handle; 25 | #[cfg(feature = "845")] 26 | let mut handle = swm.handle.enable(&mut syscon.handle); // SWM isn't enabled by default on LPC845. 27 | 28 | let sck_pin = p.pins.pio0_13.into_swm_pin(); 29 | let mosi_pin = p.pins.pio0_14.into_swm_pin(); 30 | let miso_pin = p.pins.pio0_15.into_swm_pin(); 31 | 32 | let (spi0_sck, _) = 33 | swm.movable_functions.spi0_sck.assign(sck_pin, &mut handle); 34 | let (spi0_mosi, _) = swm 35 | .movable_functions 36 | .spi0_mosi 37 | .assign(mosi_pin, &mut handle); 38 | let (spi0_miso, _) = swm 39 | .movable_functions 40 | .spi0_miso 41 | .assign(miso_pin, &mut handle); 42 | 43 | #[cfg(feature = "82x")] 44 | let spi_clock = spi::Clock::new(&(), 0); 45 | #[cfg(feature = "845")] 46 | let spi_clock = spi::Clock::new(&syscon.iosc, 0); 47 | 48 | // Enable SPI0 49 | let mut spi = p.SPI0.enable_as_master( 50 | &spi_clock, 51 | &mut syscon.handle, 52 | MODE, 53 | spi0_sck, 54 | spi0_mosi, 55 | spi0_miso, 56 | ); 57 | 58 | loop { 59 | // Cycle through colors on 16 chained APA102C LEDs 60 | loop { 61 | for r in 0..255 { 62 | let _ = spi.write(&[0, 0, 0, 0]); 63 | for _i in 0..16 { 64 | let _ = spi.write(&[0b1110_0001, 0, 0, r]); 65 | } 66 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 67 | } 68 | for b in 0..255 { 69 | let _ = spi.write(&[0, 0, 0, 0]); 70 | for _i in 0..16 { 71 | let _ = spi.write(&[0b1110_0001, b, 0, 0]); 72 | } 73 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 74 | } 75 | for g in 0..255 { 76 | let _ = spi.write(&[0, 0, 0, 0]); 77 | for _i in 0..16 { 78 | let _ = spi.write(&[0b1110_0001, 0, g, 0]); 79 | } 80 | let _ = spi.write(&[0xFF, 0xFF, 0xFF, 0xFF]); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/usart.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{cortex_m_rt::entry, prelude::*, usart, Peripherals}; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_target::rtt_init_print!(); 11 | 12 | let p = Peripherals::take().unwrap(); 13 | 14 | let swm = p.SWM.split(); 15 | let mut syscon = p.SYSCON.split(); 16 | 17 | #[cfg(feature = "82x")] 18 | let mut handle = swm.handle; 19 | #[cfg(feature = "845")] 20 | let mut handle = swm.handle.enable(&mut syscon.handle); // SWM isn't enabled by default on LPC845. 21 | 22 | #[cfg(feature = "82x")] 23 | // Set baud rate to 115200 baud 24 | // 25 | // The common peripheral clock for all UART units, U_PCLK, needs to be set 26 | // to 16 times the desired baud rate. This results in a frequency of 27 | // 1843200 Hz for U_PCLK. 28 | // 29 | // We assume the main clock runs at 12 Mhz. To get close to the desired 30 | // frequency for U_PCLK, we divide that by 6 using UARTCLKDIV, resulting in 31 | // a frequency of 2 Mhz. 32 | // 33 | // To get to the desired 1843200 Hz, we need to further divide the frequency 34 | // using the fractional baud rate generator. The fractional baud rate 35 | // generator divides the frequency by `1 + MULT/DIV`. 36 | // 37 | // DIV must always be 256. To achieve this, we need to set the UARTFRGDIV to 38 | // 0xff. MULT can then be fine-tuned to get as close as possible to the 39 | // desired value. We choose the value 22, which we write into UARTFRGMULT. 40 | // 41 | // Finally, we can set an additional divider value for the UART unit by 42 | // passing it as an argument to `BaudRate::new` (this will set the BRG 43 | // register). As we are already close enough to the desired value, we pass 44 | // 0, resulting in no further division. 45 | // 46 | // All of this is somewhat explained in the user manual, section 13.3.1. 47 | let clock_config = { 48 | syscon.uartfrg.set_clkdiv(6); 49 | syscon.uartfrg.set_frgmult(22); 50 | syscon.uartfrg.set_frgdiv(0xff); 51 | usart::Clock::new(&syscon.uartfrg, 0, 16) 52 | }; 53 | 54 | #[cfg(feature = "845")] 55 | // Set baud rate to 115200 baud 56 | let clock_config = usart::Clock::new_with_baudrate(115200); 57 | 58 | // Make the rx & tx pins available to the switch matrix API, by changing 59 | // their state using `into_swm_pin`. This is required, because we're going 60 | // to use the switch matrix to assign the USART0 functions to those pins. 61 | // 62 | // WARNING: The pinout for the lpc845brk uses tx/rx as seen from the 63 | // perspective from the serial adapter, so this is used the opposite way 64 | 65 | #[cfg(feature = "82x")] 66 | let tx_pin = p.pins.pio0_7.into_swm_pin(); 67 | #[cfg(feature = "82x")] 68 | let rx_pin = p.pins.pio0_18.into_swm_pin(); 69 | #[cfg(feature = "845")] 70 | let tx_pin = p.pins.pio0_25.into_swm_pin(); 71 | #[cfg(feature = "845")] 72 | let rx_pin = p.pins.pio0_24.into_swm_pin(); 73 | 74 | // Assign U0_RXD & U0_TXD to the rx & tx pins. On the LPCXpresso824-MAX & 75 | // LPC845-BRK development boards, they're connected to the integrated USB to 76 | // Serial converter. So by using the pins, we can use them to communicate 77 | // with a host PC, without additional hardware. 78 | let (u0_rxd, _) = swm.movable_functions.u0_rxd.assign(rx_pin, &mut handle); 79 | let (u0_txd, _) = swm.movable_functions.u0_txd.assign(tx_pin, &mut handle); 80 | 81 | // Enable USART0 82 | let mut serial = p.USART0.enable_async( 83 | &clock_config, 84 | &mut syscon.handle, 85 | u0_rxd, 86 | u0_txd, 87 | usart::Settings::default(), 88 | ); 89 | 90 | // Read all incoming bytes and echo them back. 91 | loop { 92 | let b = nb::block!(serial.read()).expect("Error reading from USART"); 93 | nb::block!(serial.write(b)).expect("Error writing to USART"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /examples/usart_dma.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | extern crate panic_rtt_target; 5 | 6 | use lpc8xx_hal::{cortex_m_rt::entry, usart, Peripherals}; 7 | 8 | #[entry] 9 | fn main() -> ! { 10 | rtt_target::rtt_init_print!(); 11 | 12 | let p = Peripherals::take().unwrap(); 13 | 14 | let swm = p.SWM.split(); 15 | let mut syscon = p.SYSCON.split(); 16 | 17 | let dma = p.DMA.enable(&mut syscon.handle); 18 | let mut swm_handle = swm.handle.enable(&mut syscon.handle); 19 | 20 | let clock_config = usart::Clock::new_with_baudrate(115200); 21 | 22 | let (u0_rxd, _) = swm 23 | .movable_functions 24 | .u0_rxd 25 | .assign(p.pins.pio0_24.into_swm_pin(), &mut swm_handle); 26 | let (u0_txd, _) = swm 27 | .movable_functions 28 | .u0_txd 29 | .assign(p.pins.pio0_25.into_swm_pin(), &mut swm_handle); 30 | 31 | let mut serial = p.USART0.enable_async( 32 | &clock_config, 33 | &mut syscon.handle, 34 | u0_rxd, 35 | u0_txd, 36 | usart::Settings::default(), 37 | ); 38 | 39 | let mut rx_channel = dma.channels.channel0; 40 | let mut tx_channel = dma.channels.channel1; 41 | 42 | static mut BUF: [u8; 4] = [0; 4]; 43 | 44 | loop { 45 | { 46 | // Sound, as the mutable reference is dropped after this block. 47 | let rx_buf = unsafe { &mut BUF }; 48 | 49 | let res = serial 50 | .rx 51 | .read_all(rx_buf, rx_channel) 52 | .start() 53 | .wait() 54 | .unwrap(); 55 | rx_channel = res.channel; 56 | serial.rx = res.source; 57 | } 58 | 59 | { 60 | // Sound, as the mutable reference is dropped after this block. 61 | let tx_buf = unsafe { &BUF }; 62 | 63 | let res = serial 64 | .tx 65 | .write_all(tx_buf, tx_channel) 66 | .start() 67 | .wait() 68 | .expect("USART write shouldn't fail"); 69 | tx_channel = res.channel; 70 | serial.tx = res.dest; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lpc8xx-hal.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | 7 | "file_exclude_patterns": [ 8 | "target/**" 9 | ] 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /memory_16_4.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 16K 4 | RAM : ORIGIN = 0x10000000, LENGTH = 4K 5 | } 6 | -------------------------------------------------------------------------------- /memory_32_8.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 32K 4 | RAM : ORIGIN = 0x10000000, LENGTH = 8K 5 | } 6 | -------------------------------------------------------------------------------- /memory_64_16.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 64K 4 | RAM : ORIGIN = 0x10000000, LENGTH = 16K 5 | } 6 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target remote | openocd -f interface/cmsis-dap.cfg -f target/openocd.cfg 2 | load 3 | # Required to work around a weird OpenOCD error message when uploading to the 4 | # LPC845-BRK. 5 | monitor reset 6 | continue 7 | -------------------------------------------------------------------------------- /openocd_82x.cfg: -------------------------------------------------------------------------------- 1 | transport select swd 2 | set WORKAREASIZE 8096 3 | 4 | source [find target/lpc8xx.cfg] 5 | 6 | adapter_khz 1000 7 | 8 | gdb_port pipe 9 | log_output openocd.log 10 | 11 | init 12 | halt 13 | -------------------------------------------------------------------------------- /openocd_84x.cfg: -------------------------------------------------------------------------------- 1 | # You might need to reset the chip after loading a new program 2 | # This can be done with `mon reset halt` 3 | 4 | transport select swd 5 | set WORKAREASIZE 8096 6 | 7 | source [find target/lpc84x.cfg] 8 | 9 | adapter_khz 1000 10 | 11 | gdb_port pipe 12 | log_output openocd.log 13 | 14 | init 15 | halt 16 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Target triple of the host machine. Set to x86-64 Linux by default, as that's 5 | # the correct value for the CI server. 6 | # 7 | # Unfotunately there doesn't seem to be a way to tell Cargo to just use the 8 | # target of the host machine, and since we override the default target in 9 | # `.cargo/config` for convenience, this variable is required. 10 | HOST_TARGET=${HOST_TARGET:-x86_64-unknown-linux-gnu} 11 | 12 | # Determine whether this build should run stable checks. This should be the case 13 | # on CI when the stable channel is used, or locally regardless of Rust version 14 | # used for the build. 15 | [ "$TRAVIS_RUST_VERSION" = stable ] && \ 16 | STABLE_CI_BUILD=true || STABLE_CI_BUILD=false 17 | [ -z "$TRAVIS_RUST_VERSION" ] && \ 18 | NO_CI_BUILD=true || NO_CI_BUILD=false 19 | ([ "$STABLE_CI_BUILD" = true ] || [ "$NO_CI_BUILD" = true ]) && \ 20 | STABLE_CHECKS=true || STABLE_CHECKS=false 21 | 22 | # Fail build, if there are any warnings. 23 | export RUSTFLAGS="-D warnings" 24 | export RUSTDOCFLAGS="-D warnings" 25 | 26 | # Check for formatting with the stable rustfmt 27 | if [ "$STABLE_CHECKS" = true ]; then 28 | # Only install rustup on stable, since it's not needed otherwise (and sometimes unavailable) 29 | rustup component add rustfmt 30 | cargo fmt -- --check 31 | fi 32 | 33 | function build() { 34 | TARGET=$1 35 | 36 | echo "" 37 | echo "### Building target $TARGET" 38 | echo "" 39 | 40 | # Only run trybuild on the stable channel. Otherwise changes to compiler 41 | # output will break the build, until they propagate all the way from nightly 42 | # to stable. 43 | [ "$STABLE_CHECKS" = true ] && EXTRA_FEATURES=",trybuild" \ 44 | || EXTRA_FEATURES="" 45 | 46 | # Build and test HAL 47 | set -x # echo the following build commands 48 | cargo test \ 49 | --verbose \ 50 | --features=$TARGET,no-target-warning$EXTRA_FEATURES \ 51 | --target=$HOST_TARGET 52 | cargo build --verbose --features=$TARGET-rt,no-target-warning --examples 53 | cargo doc --features=$TARGET-rt,no-target-warning 54 | 55 | # Build test suite 56 | ( 57 | cd test-suite 58 | cargo build --tests --features=$TARGET 59 | ) 60 | 61 | set +x # disable command echo-ing 62 | } 63 | 64 | build 82x 65 | build 845 66 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Run unit tests (from the test-suite/ directory) directly on the device 5 | # 6 | # If called without argments, unit tests for all targets are run. This requires 7 | # all required dev boards to be connected. 8 | # 9 | # If only one target should be tested, it must be passed as an argument: 10 | # - ./scripts/test.sh 82x 11 | # - ./scripts/test.sh 845 12 | 13 | # The user can optionally pass the target to run the tests for. 14 | TARGET=$1 15 | 16 | function test() { 17 | TARGET=$1 18 | 19 | echo "" 20 | echo "### Testing target $TARGET" 21 | echo "" 22 | 23 | # Select probe-run configuration 24 | [ "$TARGET" == "82x" ] && \ 25 | export PROBE_RUN_CHIP=LPC824M201JHI33 && \ 26 | export PROBE_RUN_PROBE=0d28:0204 27 | [ "$TARGET" == "845" ] && \ 28 | export PROBE_RUN_CHIP=LPC845M301JHI48 && \ 29 | export PROBE_RUN_PROBE=1fc9:0132 30 | 31 | # Set linker configuration. We have to do this from here, using an 32 | # environment variable, as otherwise the different Cargo configuration files 33 | # will interact in weird ways. Setting the rustc flags in an environment 34 | # variable overrides all `rustflags` keys in Cargo configuration, so we have 35 | # full control here. 36 | export RUSTFLAGS="\ 37 | -C linker=flip-link \ 38 | -C link-arg=-Tlink.x \ 39 | -C link-arg=-Tdefmt.x 40 | " 41 | 42 | ( 43 | cd test-suite && 44 | cargo test -p tests --features="$TARGET") 45 | } 46 | 47 | if [ -n "$TARGET" ]; then 48 | test $TARGET 49 | else 50 | test "82x" 51 | test "845" 52 | fi 53 | -------------------------------------------------------------------------------- /src/clock.rs: -------------------------------------------------------------------------------- 1 | //! Common types for system clocks 2 | //! 3 | //! This module defines types that are helpful for working with system clocks. 4 | 5 | /// Represents a number of ticks of a given clock 6 | /// 7 | /// This struct is used to represent an amount of time, a duration, but in a 8 | /// low-level way that hardware peripherals can understand and handle. It is 9 | /// meant to be a common denominator that higher-level time APIs can be built on 10 | /// top of. 11 | pub struct Ticks<'clock, C: 'clock> { 12 | /// The number of ticks 13 | pub value: u32, 14 | 15 | /// Reference to the clock 16 | /// 17 | /// For many clocks, it's possible to change their frequency. If this were 18 | /// to be done after an instance of `Ticks` had been created, that would 19 | /// invalidate the `Ticks` instance, as the same number of ticks would 20 | /// suddenly represent a different duration of time. 21 | /// 22 | /// This reference exists to prevent this. Any change to the configuration 23 | /// of a clock would presumably require a mutable reference, which means as 24 | /// long as this shared reference to the clock exists, the compiler will 25 | /// prevent the clock frequency from being changed. 26 | pub clock: &'clock C, 27 | } 28 | 29 | impl<'clock, Clock> Clone for Ticks<'clock, Clock> { 30 | fn clone(&self) -> Self { 31 | Ticks { 32 | value: self.value, 33 | clock: self.clock, 34 | } 35 | } 36 | } 37 | 38 | impl<'clock, Clock> Copy for Ticks<'clock, Clock> {} 39 | 40 | /// Implemented by clocks that can return a frequency 41 | /// 42 | /// Implementations of this trait might be very simple, for clocks that run at 43 | /// one specific frequency. Or they might be more complex, for clocks whose 44 | /// frequency can be configured. 45 | /// 46 | /// Some clocks might not have an implementation of this trait at all. An 47 | /// example of this might be a type that represents an external clock that is 48 | /// fed into the microcontroller via a pin. 49 | pub trait Frequency { 50 | /// The frequency of the clock in Hz 51 | /// 52 | /// This method must never return `0`. 53 | fn hz(&self) -> u32; 54 | } 55 | 56 | /// Marker trait that identifies a clock as currently being enabled 57 | /// 58 | /// A clock that is always enabled can just implement this trait 59 | /// unconditionally. Clocks that can be disabled can use a different type or a 60 | /// type parameter to implement this trait conditionally. 61 | /// 62 | /// HAL users will typically use this trait to ensure that a clock that is 63 | /// passed as a parameter is enabled. 64 | /// 65 | /// # Examples 66 | /// 67 | /// This is a function that takes a clock. The function uses this trait to 68 | /// ensure the passed clock is enabled. 69 | /// 70 | /// ``` rust 71 | /// use lpc8xx_hal::clock; 72 | /// 73 | /// fn use_clock(clock: C) where C: clock::Frequency + clock::Enabled { 74 | /// // do something with the clock 75 | /// } 76 | /// ``` 77 | /// 78 | /// The following example shows how to use a type parameter to track whether a 79 | /// clock is enabled, and implement the `Enabled` trait conditionally. 80 | /// 81 | /// ``` rust 82 | /// use lpc8xx_hal::{ 83 | /// clock, 84 | /// init_state, 85 | /// }; 86 | /// 87 | /// 88 | /// struct MyClock { 89 | /// _state: State, 90 | /// } 91 | /// 92 | /// impl MyClock { 93 | /// /// Consume the instance with disabled state, return one with enabled 94 | /// /// state. 95 | /// pub fn enable(self) -> MyClock { 96 | /// // Enable the clock 97 | /// // ... 98 | /// 99 | /// MyClock { 100 | /// _state: init_state::Enabled(()), 101 | /// } 102 | /// } 103 | /// } 104 | /// 105 | /// impl clock::Enabled for MyClock {} 106 | /// ``` 107 | pub trait Enabled {} 108 | -------------------------------------------------------------------------------- /src/ctimer/channel.rs: -------------------------------------------------------------------------------- 1 | //! Contains types related to CTIMER PWM channels 2 | 3 | use core::{convert::Infallible, marker::PhantomData}; 4 | 5 | use embedded_hal::PwmPin; 6 | use embedded_hal_alpha::pwm::blocking::PwmPin as PwmPinAlpha; 7 | 8 | use crate::{ 9 | init_state::Enabled, 10 | pac::{ 11 | ctimer0::{MR, MSR}, 12 | CTIMER0, 13 | }, 14 | reg_proxy::RegProxy, 15 | }; 16 | 17 | use self::state::Attached; 18 | 19 | /// A CTIMER PWM channel 20 | pub struct Channel { 21 | mr: RegProxy, 22 | msr: RegProxy, 23 | channel: PhantomData, 24 | peripheral_state: PhantomData, 25 | _state: PhantomData, 26 | } 27 | 28 | impl Channel { 29 | pub(super) fn new() -> Self { 30 | Self { 31 | mr: RegProxy::new(), 32 | msr: RegProxy::new(), 33 | channel: PhantomData, 34 | peripheral_state: PhantomData, 35 | _state: PhantomData, 36 | } 37 | } 38 | } 39 | 40 | impl PwmPin for Channel 41 | where 42 | T: Trait, 43 | { 44 | type Duty = u32; 45 | 46 | /// The behaviour of `enable` is implementation defined and does nothing in 47 | /// this implementation 48 | fn enable(&mut self) {} 49 | 50 | /// The behaviour of `disable` is implementation defined and does nothing in 51 | /// this implementation 52 | // Accessing pwmc would require some kind of lock, which is inconvenient 53 | // and would involve a hidden `CriticalSection` 54 | fn disable(&mut self) {} 55 | 56 | /// Returns the current duty cycle 57 | fn get_duty(&self) -> Self::Duty { 58 | self.msr[T::ID as usize].read().match_shadow().bits() 59 | } 60 | 61 | /// Returns the maximum duty cycle value 62 | fn get_max_duty(&self) -> Self::Duty { 63 | self.mr[3].read().match_().bits() 64 | } 65 | 66 | /// Sets a new duty cycle 67 | fn set_duty(&mut self, duty: Self::Duty) { 68 | unsafe { 69 | self.msr[T::ID as usize].write(|w| w.match_shadow().bits(duty)) 70 | }; 71 | } 72 | } 73 | 74 | impl PwmPinAlpha for Channel 75 | where 76 | T: Trait, 77 | { 78 | type Error = Infallible; 79 | type Duty = u32; 80 | 81 | /// The behaviour of `enable` is implementation defined and does nothing in 82 | /// this implementation 83 | fn enable(&mut self) -> Result<(), Self::Error> { 84 | Ok(()) 85 | } 86 | 87 | /// The behaviour of `disable` is implementation defined and does nothing in 88 | /// this implementation 89 | // Accessing pwmc would require some kind of lock, which is inconvenient 90 | // and would involve a hidden `CriticalSection` 91 | fn disable(&mut self) -> Result<(), Self::Error> { 92 | Ok(()) 93 | } 94 | 95 | /// Returns the current duty cycle 96 | fn get_duty(&self) -> Result { 97 | Ok(self.msr[T::ID as usize].read().match_shadow().bits()) 98 | } 99 | 100 | /// Returns the maximum duty cycle value 101 | fn get_max_duty(&self) -> Result { 102 | Ok(self.mr[3].read().match_().bits()) 103 | } 104 | 105 | /// Sets a new duty cycle 106 | fn set_duty(&mut self, duty: Self::Duty) -> Result<(), Self::Error> { 107 | unsafe { 108 | Ok(self.msr[T::ID as usize].write(|w| w.match_shadow().bits(duty))) 109 | } 110 | } 111 | } 112 | 113 | /// Implemented for all CTIMER PWM channels 114 | pub trait Trait: private::Sealed { 115 | /// Identifies the channel 116 | const ID: u8; 117 | 118 | /// The SWM function that needs to be assigned to this channels output pin 119 | type Output; 120 | } 121 | 122 | /// Contains types that indicate which state a channel is in 123 | pub mod state { 124 | /// Indicates that a channel is detached 125 | /// 126 | /// Detached channels don't have an output function assigned and can't be 127 | /// used for PWM output. 128 | pub struct Detached; 129 | 130 | /// Indicates that a channel is attached 131 | pub struct Attached; 132 | } 133 | 134 | pub(super) mod private { 135 | pub trait Sealed {} 136 | } 137 | 138 | reg!(MR, [MR; 4], CTIMER0, mr); 139 | reg!(MSR, [MSR; 4], CTIMER0, msr); 140 | -------------------------------------------------------------------------------- /src/ctimer/gen.rs: -------------------------------------------------------------------------------- 1 | use crate::swm; 2 | 3 | use super::channel::{self, Channel}; 4 | 5 | macro_rules! channels { 6 | ( 7 | $( 8 | $channel:ident: 9 | $field: ident, 10 | $id:expr, 11 | $output:ident, 12 | $state:ident; 13 | )* 14 | ) => { 15 | /// Contains all CTIMER PWM channels 16 | /// 17 | /// Can be accessed via `CTIMER`. 18 | #[allow(missing_docs)] 19 | pub struct Channels { 20 | $(pub $field: Channel<$channel, PeripheralState, $state>,)* 21 | } 22 | 23 | impl 24 | Channels 25 | { 26 | pub(super) fn new() -> Self { 27 | Self { 28 | $($field: Channel::new(),)* 29 | } 30 | } 31 | } 32 | 33 | $( 34 | /// Identifies a CTIMER PWM channel 35 | pub struct $channel; 36 | 37 | impl channel::private::Sealed for $channel {} 38 | 39 | impl channel::Trait for $channel { 40 | const ID: u8 = $id; 41 | type Output = swm::$output; 42 | } 43 | )* 44 | }; 45 | } 46 | 47 | channels! { 48 | Channel1: channel1, 0, T0_MAT0, State1; 49 | Channel2: channel2, 1, T0_MAT1, State2; 50 | Channel3: channel3, 2, T0_MAT2, State3; 51 | } 52 | -------------------------------------------------------------------------------- /src/ctimer/mod.rs: -------------------------------------------------------------------------------- 1 | //! API for the CTIMER peripheral 2 | //! 3 | //! Currently, only PWM output functionality is implemented. 4 | //! 5 | //! # Example 6 | //! 7 | //! ```no_run 8 | //! use lpc8xx_hal::{ 9 | //! delay::Delay, 10 | //! prelude::*, 11 | //! Peripherals, 12 | //! pac::CorePeripherals, 13 | //! }; 14 | //! 15 | //! let cp = CorePeripherals::take().unwrap(); 16 | //! let p = Peripherals::take().unwrap(); 17 | //! 18 | //! let swm = p.SWM.split(); 19 | //! let mut delay = Delay::new(cp.SYST); 20 | //! let mut syscon = p.SYSCON.split(); 21 | //! 22 | //! let mut swm_handle = swm.handle.enable(&mut syscon.handle); 23 | //! 24 | //! let pwm_output = p.pins.pio1_2.into_swm_pin(); 25 | //! 26 | //! let (pwm_output, _) = swm.movable_functions.t0_mat0.assign( 27 | //! pwm_output, 28 | //! &mut swm_handle, 29 | //! ); 30 | //! 31 | //! // Use 8 bit pwm 32 | //! let ctimer = p.CTIMER0 33 | //! .enable(256, 0, &mut syscon.handle) 34 | //! .attach(pwm_output); 35 | //! 36 | //! let mut pwm_pin = ctimer.channels.channel1; 37 | //! loop { 38 | //! for i in 0..pwm_pin.get_max_duty() { 39 | //! delay.delay_ms(4_u8); 40 | //! pwm_pin.set_duty(i); 41 | //! } 42 | //! } 43 | //! ``` 44 | 45 | pub mod channel; 46 | 47 | mod gen; 48 | mod peripheral; 49 | 50 | pub use self::{ 51 | channel::Channel, 52 | gen::*, 53 | peripheral::{Channels1, Channels12, Channels123, CTIMER}, 54 | }; 55 | -------------------------------------------------------------------------------- /src/delay.rs: -------------------------------------------------------------------------------- 1 | //! API for delays with the systick timer 2 | //! 3 | //! Please be aware of potential overflows when using `delay_us`. 4 | //! E.g. at 30MHz the maximum delay is 146 seconds. 5 | //! 6 | //! # Example 7 | //! 8 | //! ``` no_run 9 | //! use lpc8xx_hal::{ 10 | //! prelude::*, 11 | //! delay::Delay, 12 | //! pac::CorePeripherals, 13 | //! }; 14 | //! 15 | //! let mut cp = CorePeripherals::take().unwrap(); 16 | //! 17 | //! let mut delay = Delay::new(cp.SYST); 18 | //! loop { 19 | //! delay.delay_ms(1_000_u16); 20 | //! } 21 | //! ``` 22 | 23 | use cortex_m::peripheral::syst::SystClkSource; 24 | 25 | use crate::pac::SYST; 26 | use embedded_hal::blocking::delay::{DelayMs, DelayUs}; 27 | use embedded_hal_alpha::delay::blocking::DelayUs as DelayUsAlpha; 28 | use void::Void; 29 | 30 | const SYSTICK_RANGE: u32 = 0x0100_0000; 31 | const SYSTEM_CLOCK: u32 = 12_000_000; 32 | 33 | /// System timer (SysTick) as a delay provider 34 | /// 35 | /// # `embedded-hal` traits 36 | /// - [`embedded_hal::blocking::delay::DelayUs`] 37 | /// - [`embedded_hal::blocking::delay::DelayMs`] 38 | /// 39 | /// [`embedded_hal::blocking::delay::DelayUs`]: #impl-DelayUs%3Cu32%3E 40 | /// [`embedded_hal::blocking::delay::DelayMs`]: #impl-DelayMs%3Cu32%3E 41 | #[derive(Clone)] 42 | pub struct Delay { 43 | scale: u32, 44 | } 45 | 46 | impl Delay { 47 | /// Configures the system timer (SysTick) as a delay provider 48 | pub fn new(mut syst: SYST) -> Self { 49 | assert!(SYSTEM_CLOCK >= 1_000_000); 50 | let scale = SYSTEM_CLOCK / 1_000_000; 51 | syst.set_clock_source(SystClkSource::Core); 52 | 53 | syst.set_reload(SYSTICK_RANGE - 1); 54 | syst.clear_current(); 55 | syst.enable_counter(); 56 | 57 | Delay { scale } 58 | // As access to the count register is possible without a reference to the systick, we can 59 | // safely clone the enabled instance. 60 | } 61 | } 62 | 63 | impl DelayMs for Delay { 64 | /// Pauses execution for `ms` milliseconds 65 | // At 30 MHz (the maximum frequency), calling delay_us with ms * 1_000 directly overflows at 0x418937 (over the max u16 value) 66 | // So we implement a separate, higher level, delay loop 67 | fn delay_ms(&mut self, mut ms: u32) { 68 | const MAX_MS: u32 = 0x0000_FFFF; 69 | while ms != 0 { 70 | let current_ms = if ms <= MAX_MS { ms } else { MAX_MS }; 71 | DelayUs::delay_us(self, current_ms * 1_000); 72 | ms -= current_ms; 73 | } 74 | } 75 | } 76 | 77 | impl DelayMs for Delay { 78 | /// Pauses execution for `ms` milliseconds 79 | fn delay_ms(&mut self, ms: u16) { 80 | // Call delay_us directly, since we don't have to use the additional 81 | // delay loop the u32 variant uses 82 | DelayUs::delay_us(self, ms as u32 * 1_000); 83 | } 84 | } 85 | 86 | impl DelayMs for Delay { 87 | /// Pauses execution for `ms` milliseconds 88 | fn delay_ms(&mut self, ms: u8) { 89 | DelayMs::delay_ms(self, ms as u16); 90 | } 91 | } 92 | 93 | // At 30MHz (the maximum frequency), this overflows at approx. 2^32 / 30 = 146 seconds 94 | impl DelayUs for Delay { 95 | /// Pauses execution for `us` microseconds 96 | fn delay_us(&mut self, us: u32) { 97 | // The SysTick Reload Value register supports values between 1 and 0x00FFFFFF. 98 | // Here half the maximum is used so we have some play if there's a long running interrupt. 99 | const MAX_TICKS: u32 = 0x007F_FFFF; 100 | 101 | let mut total_ticks = us * self.scale; 102 | 103 | while total_ticks != 0 { 104 | let current_ticks = if total_ticks <= MAX_TICKS { 105 | total_ticks 106 | } else { 107 | MAX_TICKS 108 | }; 109 | 110 | let start_count = SYST::get_current(); 111 | total_ticks -= current_ticks; 112 | 113 | // Use the wrapping subtraction and the modulo to deal with the systick wrapping around 114 | // from 0 to 0xFFFF 115 | while (start_count.wrapping_sub(SYST::get_current()) 116 | % SYSTICK_RANGE) 117 | < current_ticks 118 | {} 119 | } 120 | } 121 | } 122 | 123 | impl DelayUsAlpha for Delay { 124 | type Error = Void; 125 | 126 | /// Pauses execution for `us` microseconds 127 | fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { 128 | Ok(DelayUs::delay_us(self, us)) 129 | } 130 | } 131 | 132 | impl DelayUs for Delay { 133 | /// Pauses execution for `us` microseconds 134 | fn delay_us(&mut self, us: u16) { 135 | DelayUs::delay_us(self, us as u32) 136 | } 137 | } 138 | 139 | impl DelayUs for Delay { 140 | /// Pauses execution for `us` microseconds 141 | fn delay_us(&mut self, us: u8) { 142 | DelayUs::delay_us(self, us as u32) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/dma/buffer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | pac::dma0::channel::xfercfg::{DSTINC_A, SRCINC_A}, 3 | void::Void, 4 | }; 5 | 6 | use super::{Dest, Source}; 7 | 8 | impl crate::private::Sealed for &'static [u8] {} 9 | 10 | impl Source for &'static [u8] { 11 | type Error = Void; 12 | 13 | fn is_valid(&self) -> bool { 14 | self.len() <= 1024 15 | } 16 | 17 | fn is_empty(&self) -> bool { 18 | self.len() == 0 19 | } 20 | 21 | fn increment(&self) -> SRCINC_A { 22 | SRCINC_A::WIDTH_X_1 23 | } 24 | 25 | fn transfer_count(&self) -> Option { 26 | if self.is_empty() { 27 | None 28 | } else { 29 | // The cast should be fine, as DMA buffers are restricted to a 30 | // length of 1024. 31 | Some(self.len() as u16 - 1) 32 | } 33 | } 34 | 35 | fn end_addr(&self) -> *const u8 { 36 | // Sound, as we stay within the bounds of the slice. 37 | unsafe { self.as_ptr().add(self.len() - 1) } 38 | } 39 | 40 | fn finish(&mut self) -> nb::Result<(), Self::Error> { 41 | Ok(()) 42 | } 43 | } 44 | 45 | impl crate::private::Sealed for &'static mut [u8] {} 46 | 47 | impl Dest for &'static mut [u8] { 48 | /// The error that can occur while waiting for the destination to be idle 49 | type Error = Void; 50 | 51 | fn is_valid(&self) -> bool { 52 | self.len() <= 1024 53 | } 54 | 55 | fn is_full(&self) -> bool { 56 | self.len() == 0 57 | } 58 | 59 | fn increment(&self) -> DSTINC_A { 60 | DSTINC_A::WIDTH_X_1 61 | } 62 | 63 | fn transfer_count(&self) -> Option { 64 | if self.is_full() { 65 | None 66 | } else { 67 | // The cast should be fine, as DMA buffers are restricted to a 68 | // length of 1024. 69 | Some(self.len() as u16 - 1) 70 | } 71 | } 72 | 73 | fn end_addr(&mut self) -> *mut u8 { 74 | // Sound, as we stay within the bounds of the slice. 75 | unsafe { self.as_mut_ptr().add(self.len() - 1) } 76 | } 77 | 78 | fn finish(&mut self) -> nb::Result<(), Self::Error> { 79 | Ok(()) 80 | } 81 | } 82 | 83 | pub(crate) struct Buffer { 84 | ptr: *mut u8, 85 | len: usize, 86 | } 87 | 88 | impl Buffer { 89 | /// Create a `Buffer` from a static slice 90 | /// 91 | /// # Unsafety 92 | /// 93 | /// The caller must make sure that the create `Buffer` instance is not used 94 | /// in a way that would interfere with the nature or usage of the slice. For 95 | /// example: 96 | /// 97 | /// - If the `Buffer` instance is used as a DMA destination, the caller must 98 | /// prevent race conditions by making sure no one else writes to the 99 | /// slice. 100 | /// - If the `Buffer` instance is used as a DMA destination, it is the 101 | /// caller's responsibility to only pass a reference to a mutable slice, 102 | /// even though this method accepts references to immutable slices. 103 | pub(crate) unsafe fn new(ptr: *mut u8, len: usize) -> Self { 104 | Self { ptr, len } 105 | } 106 | } 107 | 108 | impl crate::private::Sealed for Buffer {} 109 | 110 | impl Source for Buffer { 111 | type Error = Void; 112 | 113 | fn is_valid(&self) -> bool { 114 | self.len <= 1024 115 | } 116 | 117 | fn is_empty(&self) -> bool { 118 | self.len == 0 119 | } 120 | 121 | fn increment(&self) -> SRCINC_A { 122 | SRCINC_A::WIDTH_X_1 123 | } 124 | 125 | fn transfer_count(&self) -> Option { 126 | if self.is_empty() { 127 | None 128 | } else { 129 | // The cast should be fine, as DMA buffers are restricted to a 130 | // length of 1024. 131 | Some(self.len as u16 - 1) 132 | } 133 | } 134 | 135 | fn end_addr(&self) -> *const u8 { 136 | // Sound, as we stay within the bounds of the slice. 137 | unsafe { self.ptr.add(self.len - 1) } 138 | } 139 | 140 | fn finish(&mut self) -> nb::Result<(), Self::Error> { 141 | Ok(()) 142 | } 143 | } 144 | 145 | impl Dest for Buffer { 146 | /// The error that can occur while waiting for the destination to be idle 147 | type Error = Void; 148 | 149 | fn is_valid(&self) -> bool { 150 | self.len <= 1024 151 | } 152 | 153 | fn is_full(&self) -> bool { 154 | self.len == 0 155 | } 156 | 157 | fn increment(&self) -> DSTINC_A { 158 | DSTINC_A::WIDTH_X_1 159 | } 160 | 161 | fn transfer_count(&self) -> Option { 162 | if self.is_full() { 163 | None 164 | } else { 165 | // The cast should be fine, as DMA buffers are restricted to a 166 | // length of 1024. 167 | Some(self.len as u16 - 1) 168 | } 169 | } 170 | 171 | fn end_addr(&mut self) -> *mut u8 { 172 | // Sound, as we stay within the bounds of the slice. 173 | unsafe { self.ptr.add(self.len - 1) } 174 | } 175 | 176 | fn finish(&mut self) -> nb::Result<(), Self::Error> { 177 | Ok(()) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/dma/descriptors.rs: -------------------------------------------------------------------------------- 1 | use core::ptr; 2 | 3 | pub(super) static mut DESCRIPTORS: DescriptorTable = DescriptorTable::new(); 4 | 5 | /// The channel descriptor table 6 | /// 7 | /// Contains a descriptor for each DMA channel. 8 | #[repr(C, align(512))] 9 | pub(super) struct DescriptorTable( 10 | pub(super) [ChannelDescriptor; target::NUM_CHANNELS], 11 | ); 12 | 13 | impl DescriptorTable { 14 | /// Create a new channel descriptor table 15 | pub const fn new() -> Self { 16 | DescriptorTable([ChannelDescriptor::new(); target::NUM_CHANNELS]) 17 | } 18 | } 19 | 20 | #[derive(Clone, Copy)] 21 | #[repr(C, align(16))] 22 | pub(super) struct ChannelDescriptor { 23 | config: u32, 24 | pub(super) source_end: *const u8, 25 | pub(super) dest_end: *mut u8, 26 | next_desc: *const ChannelDescriptor, 27 | } 28 | 29 | impl ChannelDescriptor { 30 | const fn new() -> Self { 31 | ChannelDescriptor { 32 | config: 0, 33 | source_end: ptr::null(), 34 | dest_end: ptr::null_mut(), 35 | next_desc: ptr::null(), 36 | } 37 | } 38 | } 39 | 40 | // `ChannelDescriptor` contains raw pointers, therefore `Send` is not derived 41 | // automatically. I really see no reason why `ChannelDescriptor` shouldn't be 42 | // `Send` though, and it needs to be `Send`, so one can put it into a 43 | // `cortex_m::interrupt::Mutex`. 44 | unsafe impl Send for ChannelDescriptor {} 45 | 46 | #[cfg(feature = "82x")] 47 | mod target { 48 | pub const NUM_CHANNELS: usize = 18; 49 | } 50 | 51 | #[cfg(feature = "845")] 52 | mod target { 53 | pub const NUM_CHANNELS: usize = 25; 54 | } 55 | -------------------------------------------------------------------------------- /src/dma/gen.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | init_state::{Disabled, Enabled}, 3 | pac::{ 4 | self, 5 | dma0::channel::{CFG, XFERCFG}, 6 | }, 7 | reg_proxy::RegProxy, 8 | }; 9 | 10 | use super::{ 11 | channels::{self, Channel}, 12 | descriptors::DescriptorTable, 13 | }; 14 | 15 | macro_rules! channels { 16 | ($($field:ident, $name:ident, $index:expr, $cfg:ident, $xfercfg:ident;)*) => { 17 | /// Provides access to all channels 18 | /// 19 | /// This struct is part of the [`DMA`] struct. 20 | /// 21 | /// [`DMA`]: struct.DMA.html 22 | #[allow(missing_docs)] 23 | pub struct Channels { 24 | $(pub $field: Channel<$name, State>,)* 25 | } 26 | 27 | impl Channels { 28 | pub(super) fn new(descriptors: &'static mut DescriptorTable) 29 | -> Self 30 | { 31 | let mut descriptors = (&mut descriptors.0).into_iter(); 32 | 33 | Channels { 34 | $( 35 | $field: Channel { 36 | ty : $name(()), 37 | _state : Disabled, 38 | descriptor: descriptors.next().unwrap(), 39 | 40 | cfg : RegProxy::new(), 41 | xfercfg: RegProxy::new(), 42 | }, 43 | )* 44 | } 45 | } 46 | 47 | pub(super) fn enable(self) -> Channels { 48 | Channels { 49 | $( 50 | $field: self.$field.enable(), 51 | )* 52 | } 53 | } 54 | } 55 | 56 | impl Channels { 57 | pub(super) fn disable(self) -> Channels { 58 | Channels { 59 | $( 60 | $field: self.$field.disable(), 61 | )* 62 | } 63 | } 64 | } 65 | 66 | 67 | $( 68 | /// This struct is an internal implementation detail 69 | pub struct $xfercfg; 70 | 71 | reg_cluster!($xfercfg, XFERCFG, pac::DMA0, $field, xfercfg); 72 | 73 | /// This struct is an interal implementation detail 74 | pub struct $cfg; 75 | 76 | reg_cluster!($cfg, CFG, pac::DMA0, $field, cfg); 77 | 78 | /// Identifies a DMA channel 79 | pub struct $name(()); 80 | 81 | impl channels::Instance for $name { 82 | const INDEX: usize = $index; 83 | const FLAG : u32 = 0x1 << Self::INDEX; 84 | 85 | type Cfg = $cfg; 86 | type Xfercfg = $xfercfg; 87 | } 88 | )* 89 | } 90 | } 91 | 92 | #[cfg(feature = "82x")] 93 | // The channels must always be specified in order, from lowest to highest, as 94 | // the channel descriptors are assigned based on that order. 95 | channels!( 96 | channel0 , Channel0 , 0, CFG0 , XFERCFG0 ; 97 | channel1 , Channel1 , 1, CFG1 , XFERCFG1 ; 98 | channel2 , Channel2 , 2, CFG2 , XFERCFG2 ; 99 | channel3 , Channel3 , 3, CFG3 , XFERCFG3 ; 100 | channel4 , Channel4 , 4, CFG4 , XFERCFG4 ; 101 | channel5 , Channel5 , 5, CFG5 , XFERCFG5 ; 102 | channel6 , Channel6 , 6, CFG6 , XFERCFG6 ; 103 | channel7 , Channel7 , 7, CFG7 , XFERCFG7 ; 104 | channel8 , Channel8 , 8, CFG8 , XFERCFG8 ; 105 | channel9 , Channel9 , 9, CFG9 , XFERCFG9 ; 106 | channel10, Channel10, 10, CFG10, XFERCFG10; 107 | channel11, Channel11, 11, CFG11, XFERCFG11; 108 | channel12, Channel12, 12, CFG12, XFERCFG12; 109 | channel13, Channel13, 13, CFG13, XFERCFG13; 110 | channel14, Channel14, 14, CFG14, XFERCFG14; 111 | channel15, Channel15, 15, CFG15, XFERCFG15; 112 | channel16, Channel16, 16, CFG16, XFERCFG16; 113 | channel17, Channel17, 17, CFG17, XFERCFG17; 114 | ); 115 | 116 | #[cfg(feature = "845")] 117 | // The channels must always be specified in order, from lowest to highest, as 118 | // the channel descriptors are assigned based on that order. 119 | channels!( 120 | channel0 , Channel0 , 0, CFG0 , XFERCFG0 ; 121 | channel1 , Channel1 , 1, CFG1 , XFERCFG1 ; 122 | channel2 , Channel2 , 2, CFG2 , XFERCFG2 ; 123 | channel3 , Channel3 , 3, CFG3 , XFERCFG3 ; 124 | channel4 , Channel4 , 4, CFG4 , XFERCFG4 ; 125 | channel5 , Channel5 , 5, CFG5 , XFERCFG5 ; 126 | channel6 , Channel6 , 6, CFG6 , XFERCFG6 ; 127 | channel7 , Channel7 , 7, CFG7 , XFERCFG7 ; 128 | channel8 , Channel8 , 8, CFG8 , XFERCFG8 ; 129 | channel9 , Channel9 , 9, CFG9 , XFERCFG9 ; 130 | channel10, Channel10, 10, CFG10, XFERCFG10; 131 | channel11, Channel11, 11, CFG11, XFERCFG11; 132 | channel12, Channel12, 12, CFG12, XFERCFG12; 133 | channel13, Channel13, 13, CFG13, XFERCFG13; 134 | channel14, Channel14, 14, CFG14, XFERCFG14; 135 | channel15, Channel15, 15, CFG15, XFERCFG15; 136 | channel16, Channel16, 16, CFG16, XFERCFG16; 137 | channel17, Channel17, 17, CFG17, XFERCFG17; 138 | channel18, Channel18, 18, CFG18, XFERCFG18; 139 | channel19, Channel19, 19, CFG19, XFERCFG19; 140 | channel20, Channel20, 20, CFG20, XFERCFG20; 141 | channel21, Channel21, 21, CFG21, XFERCFG21; 142 | channel22, Channel22, 22, CFG22, XFERCFG22; 143 | channel23, Channel23, 23, CFG23, XFERCFG23; 144 | channel24, Channel24, 24, CFG24, XFERCFG24; 145 | ); 146 | -------------------------------------------------------------------------------- /src/dma/mod.rs: -------------------------------------------------------------------------------- 1 | //! API for Direct Memory Access (DMA) 2 | //! 3 | //! The entry point to this API is the [`DMA`] struct. 4 | //! 5 | //! The DMA peripheral is described in the following user manuals: 6 | //! - LPC82x user manual, chapter 12 7 | //! - LPC84x user manual, chapter 16 8 | //! 9 | //! [`DMA`]: struct.DMA.html 10 | 11 | mod buffer; 12 | mod descriptors; 13 | mod gen; 14 | mod peripheral; 15 | 16 | pub mod channels; 17 | pub mod transfer; 18 | 19 | pub use self::{ 20 | channels::Channel, 21 | gen::*, 22 | peripheral::DMA, 23 | transfer::{Dest, Payload, Source, Transfer}, 24 | }; 25 | 26 | pub(crate) use self::buffer::Buffer; 27 | -------------------------------------------------------------------------------- /src/dma/peripheral.rs: -------------------------------------------------------------------------------- 1 | use crate::{init_state, pac, syscon}; 2 | 3 | use super::Channels; 4 | 5 | /// Entry point to the DMA API 6 | /// 7 | /// Controls enabling/disabling the DMA peripheral, and provides access to all 8 | /// DMA channels via the `channels` field. 9 | /// 10 | /// You can gain access to an instance of this struct via [`Peripherals`]. 11 | /// 12 | /// [`Peripherals`]: ../struct.Peripherals.html 13 | pub struct DMA { 14 | dma: pac::DMA0, 15 | srambase: u32, 16 | 17 | /// The DMA channels 18 | pub channels: Channels, 19 | } 20 | 21 | impl DMA { 22 | pub(crate) fn new(dma: pac::DMA0) -> Self { 23 | let descriptors = unsafe { &mut super::descriptors::DESCRIPTORS }; 24 | let srambase = descriptors as *mut _ as u32; 25 | 26 | Self { 27 | dma, 28 | srambase, 29 | channels: Channels::new(descriptors), 30 | } 31 | } 32 | 33 | /// Enable the DMA controller 34 | /// 35 | /// This method is only available, if `DMA` is in the [`Disabled`] state. 36 | /// Code attempting to call this method when this is not the case will not 37 | /// compile. 38 | /// 39 | /// Consumes this instance of `DMA` and returns another instance with its 40 | /// `State` parameter set to [`Enabled`]. 41 | /// 42 | /// [`Disabled`]: ../init_state/struct.Disabled.html 43 | /// [`Enabled`]: ../init_state/struct.Enabled.html 44 | pub fn enable( 45 | self, 46 | syscon: &mut syscon::Handle, 47 | ) -> DMA { 48 | syscon.enable_clock(&self.dma); 49 | 50 | // Set descriptor table address 51 | // 52 | // See user manual, section 12.6.3. 53 | self.dma 54 | .srambase 55 | .write(|w| unsafe { w.bits(self.srambase) }); 56 | 57 | // Enable the DMA controller 58 | // 59 | // See user manual, section 12.6.1. 60 | self.dma.ctrl.write(|w| w.enable().enabled()); 61 | 62 | DMA { 63 | dma: self.dma, 64 | srambase: self.srambase, 65 | channels: self.channels.enable(), 66 | } 67 | } 68 | } 69 | 70 | impl DMA { 71 | /// Disable the DMA controller 72 | /// 73 | /// This method is only available, if `DMA` is in the [`Enabled`] state. 74 | /// Code attempting to call this method when this is not the case will not 75 | /// compile. 76 | /// 77 | /// Consumes this instance of `DMA` and returns another instance with its 78 | /// `State` parameter set to [`Disabled`]. 79 | /// 80 | /// [`Enabled`]: ../init_state/struct.Enabled.html 81 | /// [`Disabled`]: ../init_state/struct.Disabled.html 82 | pub fn disable( 83 | self, 84 | syscon: &mut syscon::Handle, 85 | ) -> DMA { 86 | syscon.disable_clock(&self.dma); 87 | 88 | DMA { 89 | dma: self.dma, 90 | srambase: self.srambase, 91 | channels: self.channels.disable(), 92 | } 93 | } 94 | } 95 | 96 | impl DMA { 97 | /// Return the raw peripheral 98 | /// 99 | /// This method serves as an escape hatch from the HAL API. It returns the 100 | /// raw peripheral, allowing you to do whatever you want with it, without 101 | /// limitations imposed by the API. 102 | /// 103 | /// If you are using this method because a feature you need is missing from 104 | /// the HAL API, please [open an issue] or, if an issue for your feature 105 | /// request already exists, comment on the existing issue, so we can 106 | /// prioritize it accordingly. 107 | /// 108 | /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues 109 | pub fn free(self) -> pac::DMA0 { 110 | self.dma 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/i2c/clock.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | use crate::syscon::{self, clock_source::PeripheralClockSelector}; 4 | 5 | /// Contains the clock configuration for an I2C instance 6 | pub struct Clock { 7 | pub(crate) divval: u16, 8 | pub(crate) mstsclhigh: u8, 9 | pub(crate) mstscllow: u8, 10 | pub(crate) _clock: PhantomData, 11 | } 12 | 13 | impl Clock 14 | where 15 | C: ClockSource, 16 | { 17 | /// Create the clock config for the I2C peripheral 18 | /// 19 | /// `mstclhigh` and `mstcllow` have to be between 2-9. 20 | pub fn new(_: &C, divval: u16, mstsclhigh: u8, mstscllow: u8) -> Self { 21 | assert!(mstsclhigh > 1 && mstsclhigh < 10); 22 | assert!(mstscllow > 1 && mstscllow < 10); 23 | Self { 24 | divval, 25 | mstsclhigh: mstsclhigh - 2, 26 | mstscllow: mstscllow - 2, 27 | _clock: PhantomData, 28 | } 29 | } 30 | } 31 | 32 | /// Implemented for I2C clock sources 33 | pub trait ClockSource: private::Sealed { 34 | /// Select the clock source 35 | /// 36 | /// This method is used by the I2C API internally. It should not be relevant 37 | /// to most users. 38 | /// 39 | /// The `selector` argument should not be required to implement this trait, 40 | /// but it makes sure that the caller has access to the peripheral they are 41 | /// selecting the clock for. 42 | fn select(selector: &S, handle: &mut syscon::Handle) 43 | where 44 | S: PeripheralClockSelector; 45 | } 46 | 47 | #[cfg(feature = "82x")] 48 | mod target { 49 | use core::marker::PhantomData; 50 | 51 | use crate::syscon; 52 | 53 | use super::{Clock, ClockSource}; 54 | 55 | impl super::private::Sealed for () {} 56 | 57 | impl ClockSource for () { 58 | fn select(_: &S, _: &mut syscon::Handle) { 59 | // nothing to do; `()` represents the clock that is selected by 60 | // default 61 | } 62 | } 63 | 64 | impl Clock<()> { 65 | /// Create a new I2C clock configuration for 400 kHz 66 | /// 67 | /// Assumes the internal oscillator runs at 12 MHz. 68 | pub fn new_400khz() -> Self { 69 | Self { 70 | divval: 5, 71 | mstsclhigh: 0, 72 | mstscllow: 1, 73 | _clock: PhantomData, 74 | } 75 | } 76 | } 77 | } 78 | 79 | #[cfg(feature = "845")] 80 | mod target { 81 | use core::marker::PhantomData; 82 | 83 | use crate::syscon::{ 84 | self, 85 | clock_source::{PeripheralClock, PeripheralClockSelector}, 86 | IOSC, 87 | }; 88 | 89 | use super::{Clock, ClockSource}; 90 | 91 | impl super::private::Sealed for T where T: PeripheralClock {} 92 | impl ClockSource for T 93 | where 94 | T: PeripheralClock, 95 | { 96 | fn select(selector: &S, handle: &mut syscon::Handle) 97 | where 98 | S: PeripheralClockSelector, 99 | { 100 | T::select(selector, handle); 101 | } 102 | } 103 | 104 | impl Clock { 105 | /// Create a new I2C clock configuration for 400 kHz 106 | /// 107 | /// Assumes the internal oscillator runs at 12 MHz. 108 | pub fn new_400khz() -> Self { 109 | Self { 110 | divval: 5, 111 | mstsclhigh: 0, 112 | mstscllow: 1, 113 | _clock: PhantomData, 114 | } 115 | } 116 | } 117 | } 118 | 119 | mod private { 120 | pub trait Sealed {} 121 | } 122 | -------------------------------------------------------------------------------- /src/i2c/error.rs: -------------------------------------------------------------------------------- 1 | use super::{master, Instance}; 2 | 3 | /// I2C error 4 | #[derive(Debug, Eq, PartialEq)] 5 | #[non_exhaustive] 6 | pub enum Error { 7 | /// Event Timeout 8 | /// 9 | /// Corresponds to the EVENTTIMEOUT flag in the STAT register. 10 | EventTimeout, 11 | 12 | /// Master Arbitration Loss 13 | /// 14 | /// Corresponds to the MSTARBLOSS flag in the STAT register. 15 | MasterArbitrationLoss, 16 | 17 | /// Master Start/Stop Error 18 | /// 19 | /// Corresponds to the MSTSTSTPERR flag in the STAT register. 20 | MasterStartStopError, 21 | 22 | /// Monitor Overflow 23 | /// 24 | /// Corresponds to the MONOV flag in the STAT register. 25 | MonitorOverflow, 26 | 27 | /// SCL Timeout 28 | /// 29 | /// Corresponds to the SCLTIMEOUT flag in the STAT register. 30 | SclTimeout, 31 | 32 | /// The I2C code encountered an unexpected hardware state 33 | UnexpectedState { 34 | /// The state that was expected 35 | expected: master::State, 36 | 37 | /// The state that was actually set 38 | /// 39 | /// The `Ok` variant represents a valid state. The `Err` variant 40 | /// represents an invalid bit pattern in the MSTSTATE field. 41 | actual: Result, 42 | }, 43 | 44 | /// An unencodable address was specified. 45 | /// 46 | /// Currently, only seven-bit addressing is implemented. 47 | AddressOutOfRange, 48 | 49 | /// While in slave mode, an unknown state was detected 50 | UnknownSlaveState(u8), 51 | } 52 | 53 | impl Error { 54 | pub(super) fn check_address(address: u8) -> Result<(), Self> { 55 | if address > 0b111_1111 { 56 | return Err(Self::AddressOutOfRange); 57 | } 58 | 59 | Ok(()) 60 | } 61 | 62 | pub(super) fn read() -> Result<(), Self> { 63 | // Sound, as we're only reading from the STAT register. 64 | let i2c = unsafe { &*I::REGISTERS }; 65 | 66 | let stat = i2c.stat.read(); 67 | 68 | // Check for error flags. If one is set, clear it and return the error. 69 | if stat.mstarbloss().bit_is_set() { 70 | i2c.stat.write(|w| w.mstarbloss().set_bit()); 71 | return Err(Self::MasterArbitrationLoss); 72 | } 73 | if stat.mstststperr().bit_is_set() { 74 | i2c.stat.write(|w| w.mstststperr().set_bit()); 75 | return Err(Self::MasterStartStopError); 76 | } 77 | if stat.monov().bit_is_set() { 78 | i2c.stat.write(|w| w.monov().set_bit()); 79 | return Err(Self::MonitorOverflow); 80 | } 81 | if stat.eventtimeout().bit_is_set() { 82 | i2c.stat.write(|w| w.eventtimeout().set_bit()); 83 | return Err(Self::EventTimeout); 84 | } 85 | if stat.scltimeout().bit_is_set() { 86 | i2c.stat.write(|w| w.scltimeout().set_bit()); 87 | return Err(Self::SclTimeout); 88 | } 89 | 90 | Ok(()) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/i2c/instances.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Deref; 2 | 3 | use crate::{ 4 | dma, 5 | pac::{self, Interrupt}, 6 | swm, 7 | syscon::{self, clock_source::PeripheralClockSelector}, 8 | }; 9 | 10 | /// Implemented for all I2C instances 11 | pub trait Instance: 12 | private::Sealed 13 | + Deref 14 | + syscon::ClockControl 15 | + syscon::ResetControl 16 | + PeripheralClockSelector 17 | { 18 | /// The interrupt that is triggered for this I2C peripheral 19 | const INTERRUPT: Interrupt; 20 | 21 | /// A pointer to this instance's register block 22 | const REGISTERS: *const pac::i2c0::RegisterBlock; 23 | 24 | /// The movable function that needs to be assigned to this I2C's SDA pin 25 | type Sda; 26 | 27 | /// The movable function that needs to be assigned to this I2C's SCL pin 28 | type Scl; 29 | 30 | /// The DMA channel used with this instance for slave mode 31 | type SlvChannel: dma::channels::Instance; 32 | 33 | /// The DMA channel used with this instance for master mode 34 | type MstChannel: dma::channels::Instance; 35 | } 36 | 37 | macro_rules! instances { 38 | ( 39 | $( 40 | $instance:ident, 41 | $clock_num:expr, 42 | $interrupt:ident, 43 | $rx:ident, 44 | $tx:ident, 45 | $slv_channel:ident, 46 | $mst_channel:ident; 47 | )* 48 | ) => { 49 | $( 50 | impl private::Sealed for pac::$instance {} 51 | 52 | impl Instance for pac::$instance { 53 | const INTERRUPT: Interrupt = Interrupt::$interrupt; 54 | const REGISTERS: *const pac::i2c0::RegisterBlock = 55 | pac::$instance::ptr(); 56 | 57 | type Sda = swm::$rx; 58 | type Scl = swm::$tx; 59 | 60 | type SlvChannel = dma::$slv_channel; 61 | type MstChannel = dma::$mst_channel; 62 | } 63 | 64 | impl PeripheralClockSelector for pac::$instance { 65 | const REGISTER_NUM: usize = $clock_num; 66 | } 67 | )* 68 | }; 69 | } 70 | 71 | #[cfg(feature = "82x")] 72 | instances!( 73 | I2C0, 5, I2C0, I2C0_SDA, I2C0_SCL, Channel10, Channel11; 74 | I2C1, 6, I2C1, I2C1_SDA, I2C1_SCL, Channel12, Channel13; 75 | I2C2, 7, I2C2, I2C2_SDA, I2C2_SCL, Channel14, Channel15; 76 | I2C3, 8, I2C3, I2C3_SDA, I2C3_SCL, Channel16, Channel17; 77 | ); 78 | 79 | #[cfg(feature = "845")] 80 | instances!( 81 | I2C0, 5, I2C0, I2C0_SDA, I2C0_SCL, Channel14, Channel15; 82 | I2C1, 6, I2C1, I2C1_SDA, I2C1_SCL, Channel16, Channel17; 83 | I2C2, 7, I2C2, I2C2_SDA, I2C2_SCL, Channel18, Channel19; 84 | I2C3, 8, I2C3, I2C3_SDA, I2C3_SCL, Channel20, Channel21; 85 | ); 86 | 87 | mod private { 88 | pub trait Sealed {} 89 | } 90 | -------------------------------------------------------------------------------- /src/i2c/interrupts.rs: -------------------------------------------------------------------------------- 1 | use super::Instance; 2 | 3 | macro_rules! interrupts { 4 | ( 5 | $( 6 | $doc:expr, 7 | $field:ident, 8 | $enable:ident, 9 | $disable:ident; 10 | )* 11 | ) => { 12 | /// Used to enable or disable I2C interrupts 13 | /// 14 | /// See [`I2C::enable_interrupts`] or [`I2C::disable_interrupts`]. 15 | /// 16 | /// [`I2C::enable_interrupts`]: struct.I2C.html#method.enable_interrupts 17 | /// [`I2C::disable_interrupts`]: struct.I2C.html#method.disable_interrupts 18 | pub struct Interrupts { 19 | $( 20 | #[doc = $doc] 21 | pub $field: bool, 22 | )* 23 | } 24 | 25 | impl Interrupts { 26 | pub(super) fn enable(&self, i2c: &I) { 27 | i2c.intenset.modify(|_, w| { 28 | $( 29 | if self.$field { 30 | w.$enable().enabled(); 31 | } 32 | )* 33 | 34 | w 35 | }) 36 | } 37 | 38 | pub(super) fn disable(&self, i2c: &I) { 39 | i2c.intenclr.write(|w| { 40 | $( 41 | if self.$field { 42 | w.$disable().set_bit(); 43 | } 44 | )* 45 | 46 | w 47 | }) 48 | } 49 | } 50 | 51 | impl Default for Interrupts { 52 | fn default() -> Self { 53 | Self { 54 | $( 55 | $field: false, 56 | )* 57 | } 58 | } 59 | } 60 | }; 61 | } 62 | 63 | interrupts!( 64 | "Master Pending", master_pending, 65 | mstpendingen, mstpendingclr; 66 | "Master Arbitration Loss", master_arbitration_loss, 67 | mstarblossen, mstarblossclr; 68 | "Master Start/Stop Error", master_start_stop_error, 69 | mstststperren, mstststperrclr; 70 | "Slave Pending", slave_pending, 71 | slvpendingen, slvpendingclr; 72 | "Slave Not Stretching", slave_not_stretching, 73 | slvnotstren, slvnotstrclr; 74 | "Slave Deselect", slave_deselect, 75 | slvdeselen, slvdeselclr; 76 | "Monitor Ready", monitor_ready, 77 | monrdyen, monrdyclr; 78 | "Monitor Overrun", monitor_overrun, 79 | monoven, monovclr; 80 | "Monitor Idle", monitor_idle, 81 | monidleen, monidleclr; 82 | "Event Timeout", event_timeout, 83 | eventtimeouten, eventtimeoutclr; 84 | "SCL Timeout", scl_timeout, 85 | scltimeouten, scltimeoutclr; 86 | ); 87 | -------------------------------------------------------------------------------- /src/i2c/mod.rs: -------------------------------------------------------------------------------- 1 | //! API for the I2C peripherals 2 | //! 3 | //! The entry point to this API is the [`I2C`] struct. 4 | //! 5 | //! The I2C peripheral is described in the following user manuals: 6 | //! - LPC82x user manual, chapter 15 7 | //! - LPC84x user manual, chapter 19 8 | //! 9 | //! # Examples 10 | //! 11 | //! Write data to an I2C slave: 12 | //! 13 | //! ``` no_run 14 | //! # let address = 0x0; 15 | //! # let data = [0; 8]; 16 | //! # 17 | //! use lpc8xx_hal::{ 18 | //! prelude::*, 19 | //! Peripherals, 20 | //! i2c, 21 | //! }; 22 | //! 23 | //! let mut p = Peripherals::take().unwrap(); 24 | //! 25 | //! let mut swm = p.SWM.split(); 26 | //! let mut syscon = p.SYSCON.split(); 27 | //! 28 | //! #[cfg(feature = "82x")] 29 | //! let mut swm_handle = swm.handle; 30 | //! #[cfg(feature = "845")] 31 | //! let mut swm_handle = swm.handle.enable(&mut syscon.handle); 32 | //! 33 | //! let (i2c0_sda, _) = swm.fixed_functions.i2c0_sda.assign( 34 | //! p.pins.pio0_11.into_swm_pin(), 35 | //! &mut swm_handle, 36 | //! ); 37 | //! let (i2c0_scl, _) = swm.fixed_functions.i2c0_scl.assign( 38 | //! p.pins.pio0_10.into_swm_pin(), 39 | //! &mut swm_handle, 40 | //! ); 41 | //! 42 | //! #[cfg(feature = "82x")] 43 | //! let clock = &(); // I2C is always powered by system clock on LPC82x 44 | //! #[cfg(feature = "845")] 45 | //! let clock = &syscon.iosc; 46 | //! 47 | //! let mut i2c = p.I2C0 48 | //! .enable( 49 | //! clock, 50 | //! i2c0_scl, 51 | //! i2c0_sda, 52 | //! &mut syscon.handle, 53 | //! ) 54 | //! .enable_master_mode( 55 | //! &i2c::Clock::new_400khz(), 56 | //! ); 57 | //! 58 | //! i2c.master.write(address, &data) 59 | //! .expect("Failed to write data"); 60 | //! ``` 61 | //! 62 | //! Please refer to the [examples in the repository] for more example code. 63 | //! 64 | //! [`I2C`]: struct.I2C.html 65 | //! [examples in the repository]: https://github.com/lpc-rs/lpc8xx-hal/tree/master/examples 66 | 67 | mod clock; 68 | mod error; 69 | mod instances; 70 | mod interrupts; 71 | mod peripheral; 72 | 73 | pub mod master; 74 | pub mod slave; 75 | 76 | pub use self::{ 77 | clock::{Clock, ClockSource}, 78 | error::Error, 79 | instances::Instance, 80 | interrupts::Interrupts, 81 | master::Master, 82 | peripheral::I2C, 83 | slave::Slave, 84 | }; 85 | -------------------------------------------------------------------------------- /src/mrt/channel.rs: -------------------------------------------------------------------------------- 1 | use crate::reg_proxy::{Reg, RegProxy}; 2 | 3 | use embedded_hal::timer::{CountDown, Periodic}; 4 | use embedded_hal_alpha::timer::{ 5 | nb::CountDown as CountDownAlpha, Periodic as PeriodicAlpha, 6 | }; 7 | use embedded_time::{clock, fraction::Fraction, Instant}; 8 | use void::Void; 9 | 10 | use super::{Ticks, Trait}; 11 | 12 | /// Represents a MRT0 channel 13 | /// 14 | /// # `embedded-hal` traits 15 | /// - [`embedded_hal::timer::CountDown`] 16 | /// 17 | /// [`embedded_hal::timer::CountDown`]: #impl-CountDown 18 | pub struct Channel(RegProxy); 19 | 20 | impl Channel 21 | where 22 | T: Trait, 23 | { 24 | pub(super) fn new() -> Self { 25 | Self(RegProxy::new()) 26 | } 27 | 28 | /// Start the timer 29 | /// 30 | /// The `reload` argument must be smaller than or equal to [`MAX_VALUE`]. 31 | /// 32 | /// [`MAX_VALUE`]: constant.MAX_VALUE.html 33 | pub fn start(&mut self, reload: Ticks) { 34 | // This stops the timer, to prevent race conditions when resetting the 35 | // interrupt bit 36 | self.0.intval.write(|w| { 37 | w.load().set_bit(); 38 | unsafe { w.ivalue().bits(0) } 39 | }); 40 | self.0.stat.write(|w| w.intflag().set_bit()); 41 | self.0 42 | .intval 43 | .write(|w| unsafe { w.ivalue().bits(reload.0 + 1) }); 44 | } 45 | 46 | /// Indicates whether the timer is running 47 | pub fn is_running(&self) -> bool { 48 | self.0.stat.read().run().is_running() 49 | } 50 | 51 | /// Returns the current timer value 52 | pub fn value(&self) -> u32 { 53 | self.0.timer.read().value().bits() 54 | } 55 | 56 | /// Returns the reload value of the timer 57 | pub fn reload_value(&self) -> u32 { 58 | self.0.intval.read().ivalue().bits() 59 | } 60 | 61 | /// Non-blockingly "waits" until the count down finishes 62 | fn wait(&mut self) -> nb::Result<(), Void> { 63 | if self.0.stat.read().intflag().is_pending_interrupt() { 64 | // Reset the interrupt flag 65 | self.0.stat.write(|w| w.intflag().set_bit()); 66 | Ok(()) 67 | } else { 68 | Err(nb::Error::WouldBlock) 69 | } 70 | } 71 | } 72 | 73 | impl CountDown for Channel 74 | where 75 | T: Trait, 76 | { 77 | /// The timer operates in clock ticks from the system clock, that means it 78 | /// runs at 12_000_000 ticks per second if you haven't changed it. 79 | /// 80 | /// It can also only use values smaller than 0x7FFFFFFF. 81 | type Time = Ticks; 82 | 83 | fn start