Crate lpc82x_hal [] [src]

LPC82x Hardware Abstraction Layer

Hardware Abstraction Layer (HAL) for the NXP LPC82x series of ARM Cortex-M0+ microcontrollers.

Using LPC82x HAL in a Library

Writing a library on top of LPC82x HAL is pretty simple. All you need to do is include it via Cargo, by adding the following to your Cargo.toml:

[dependencies]
lpc82x-hal = "0.1"

With that in place, you can just reference the crate in your Rust code, like this:

// lib.rs

extern crate lpc82x_hal;

That's it! Now you can start using the LPC82x HAL APIs.

Please note that LPC82x HAL is an implementation of embedded-hal. If your library is not specific to LPC82x, please consider depending on embedded-hal instead. Doing so means that your library will work on top of all embedded-hal implementations.

Using LPC82x HAL in an Application

To use LPC82x HAL in your application, you need to go through a bit of additional trouble. This section tries to walk you through some of the basics, but it's not a complete tutorial. Please refer to cortex-m-quickstart for additional details.

Runtime Support

Including LPC82x HAL in your application via Cargo is mostly the same as it is for libraries, but with one addition. You need to enable runtime support when including the crate in your Cargo.toml:

[dependencies.lpc82x-hal]
version  = "0.1"
features = ["rt"]

The runtime support will provide you with some basics that are required for your program to run correctly. However, it needs to know how the memory on your microcontroller is set up.

You can get that information from the user manual. To provide it to LPC82x HAL, create a file called memory.x in your project root (the directory where Cargo.toml is located). memory.x should look something like this:

This example is not tested
MEMORY
{
    FLASH : ORIGIN = 0x00000000, LENGTH = 32K
    RAM   : ORIGIN = 0x10000000, LENGTH = 8K
}

Runtime support is provided by the cortex-m-rt crate. Please refer to the cortex-m-rt documentation for additional details.

Build System

The build system needs to be set up to compile and link a binary for LPC82x. Cargo alone is not enough for this, as its support for embedded development is currently limited. Xargo exists to fill the gap in the meantime. You can install it using Cargo:

This example is not tested
$ cargo install xargo

Add a new file, Xargo.toml right next to your Cargo.toml, with the following contents:

[dependencies.core]
stage = 0

[dependencies.compiler_builtins]
stage    = 1
features = ["c", "mem"]

You might not need all those optional features of compiler_builtin, so feel free to experiment.

Additionally, you need to tell Cargo how to link your project. Create the file .cargo/config in your project directory, and add the following contents:

[target.thumbv6m-none-eabi]
rustflags = [
    "-C", "link-arg=-Tlink.x",
    "-C", "linker=arm-none-eabi-ld",
    "-Z", "linker-flavor=ld"
]

This tells Cargo to use the arm-none-eabi-gcc toolchain for linking. You need to install this separately. How to do so is dependent on your platform and is left as an exercise to the reader.

If everything is set up correctly, you can build your project with the following command:

This example is not tested
$ xargo build --release --target=thumbv6m-none-eabi

Uploading the Binary

There are many ways to upload the binary to the microcontroller. How to do this is currently beyond the scope of this documentation, but this fork of lpc21isp is known to work.

Example

The following is an example of a simple application that blinks an LED.

extern crate lpc82x;
extern crate lpc82x_hal;

use lpc82x_hal::prelude::*;
use lpc82x_hal::{
    GPIO,
    SWM,
    SYSCON,
    WKT,
};
use lpc82x_hal::clock::Ticks;
use lpc82x_hal::gpio::PIO0_3;
use lpc82x_hal::sleep::{
    self,
    Sleep,
};

// Create the struct we're going to use to access all the peripherals. This
// is unsafe, because we're only allowed to create one instance.
let mut peripherals = lpc82x::Peripherals::take().unwrap();

// Create the peripheral interfaces.
let     gpio   = GPIO::new(&mut peripherals.GPIO_PORT);
let     swm    = SWM::new(&mut peripherals.SWM);
let mut syscon = SYSCON::new(&mut peripherals.SYSCON);
let     wkt    = WKT::new(&mut peripherals.WKT);

// Other peripherals need to be initialized. Trying to use the API before
// initializing them will actually lead to compile-time errors.
let mut gpio_handle = gpio.handle.init(&mut syscon.handle);
let mut swm_handle  = swm.handle.init(&mut syscon.handle);
let mut wkt         = wkt.init(&mut syscon.handle);

// We're going to need a clock for sleeping. Let's use the IRC-derived clock
// that runs at 750 kHz.
let clock = syscon.irc_derived_clock.enable(
    &mut syscon.handle,
    syscon.irc,
    syscon.ircout,
);

// In the next step, we need to configure the pin PIO0_3 and its fixed
// function SWCLK. The API tracks the state of both of those, to prevent any
// mistakes on our side. However, since we could have changed the state of
// the hardware before initializing the API, the API can't know what state
// it is currently in.
// Let's affirm that we haven't changed anything, and that PIO0_3 and SWCLK
// are still in their initial states.
let pio0_3 = unsafe { gpio.pins.pio0_3.affirm_default_state()          };
let swclk  = unsafe { swm.fixed_functions.swclk.affirm_default_state() };

// Configure PIO0_3 as GPIO output, so we can use it to blink an LED.
let (pio0_3, _) = pio0_3
    .disable_output_function(swclk, &mut swm_handle);
let mut pio0_3 = pio0_3
    .as_unused_pin()
    .as_gpio_pin(&gpio_handle)
    .as_output();

// Let's already initialize the durations that we're going to sleep for
// between changing the LED state. We do this by specifying the number of
// clock ticks directly, but a real program could use a library that allows
// us to specify the time in milliseconds.
// Each duration also keeps a reference to the clock, as to prevent other
// parts of the program from accidentally disabling the clock, or changing
// its settings.
let high_time = Ticks { value:  37_500, clock: &clock }; //  50 ms
let low_time  = Ticks { value: 712_500, clock: &clock }; // 950 ms

// Since this is a simple example, we don't want to deal with interrupts
// here. Let's just use busy waiting as a sleeping strategy.
let mut sleep = sleep::Busy::prepare(&mut wkt);

// Blink the LED
loop {
    pio0_3.set_high();
    sleep.sleep(high_time);
    pio0_3.set_low();
    sleep.sleep(low_time);
}

References

Various places in this crate's documentation reference the LPC82x User manual, which is available from NXP.

Re-exports

pub use self::gpio::GPIO;
pub use self::pmu::PMU;
pub use self::swm::SWM;
pub use self::syscon::SYSCON;
pub use self::usart::USART;
pub use self::wkt::WKT;

Modules

clock

Common types for system clocks

gpio

API for General Purpose I/O (GPIO)

init_state

Contains types that encode the state hardware initialization

pmu

API for the Power Management Unit (PMU)

prelude

Re-exports various traits that are required to use lpc82x-hal

sleep

Higher-level sleep API

swm

APIs for the switch matrix (SWM)

syscon

API for system configuration (SYSCON)

usart

API for the USART peripherals

wkt

API for the self-wake-up timer (WKT)

Structs

CPUID

CPUID

DCB

Debug Control Block

DWT

Data Watchpoint and Trace unit

MPU

Memory Protection Unit

NVIC

Nested Vector Interrupt Controller

SCB

System Control Block

SYST

SysTick: System Timer

Enums

Interrupt

Enumeration of all the interrupts