1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! High-level API and HAL implementations for LPC81x microcontrollers.
//!
//! This library wraps the lower-level `lpc81x-pac` to provide a more ergonomic
//! interface to the LPC81x peripherals, including implementations of some of
//! the `embedded-hal` crates so that these peripheraps can be used with
//! platform-agnostic external device drivers in other crates.
//!
//! -----
//!
//! **Warning:** The interfaces in this crate are _not stable_. Any existing
//! API may see breaking changes in subsequent versions until all of the
//! peripherals have basic support and until `embedded-hal` itself is
//! stable enough to commit to compatibility with it.
//!
//! Pin to a specific version in your application to avoid unexpected breakage.
//!
//! -----
//!
//! The core concept in this library is objects representing the I/O pins.
//! LPC81x parts include a switch matrix that allow most integrated peripherals
//! to be assigned to any of the available pins, and so this library models
//! that capability by having the peripheral objects take ownership of the
//! pins they are assigned to. That allows compile-time checks to ensure that
//! only one output function is assigned to each pin, as is required by the
//! switch matrix hardware.
//!
//! The following example shows how a calling application might activate the
//! SPI0 periopheral and switch a GPIO pin to digital output mode in order to
//! serve as a chip-select signal:
//!
//! ```rust
//! // Consume the peripherals singleton. This also consumes the corresponding
//! // singleton from `lpc81x-pac`, so all peripheral access must be via
//! // this object alone.
//! let p = hal::Peripherals::take().unwrap();
//!
//! // Most of the peripheral APIs require pins as arguments. Unassigned pins
//! // initially live in p.pins and can be moved out into other devices as
//! // needed, which automatically configures the switch matrix.
//! let pins = p.pins;
//!
//! // Use GPIO pins 12 and 14 as the SCLK and MOSI signals respectively.
//! // An SCLK pin is always required to activate an SPI peripheral, but
//! // the other signals can be assigned selectively depending on the needs
//! // of the application. Must assign at least MOSI or MISO for anything
//! // useful to happen, though.
//! let spi = p
//!     .spi0
//!     .activate_as_host(
//!         pins.gpio12,
//!         hal::spi::cfg::Config {
//!             sclk_mode: embedded_hal::spi::MODE_0,
//!             bit_order: hal::spi::cfg::BitOrder::MSBFirst,
//!         },
//!     )
//!     .with_mosi(pins.gpio14);
//!
//! // GPIO pin 13 will be the chip select signal. Calling to_digital_output
//! // consumes the unassigned pin and returns an assigned pin that has been
//! // configured to act as a digital output.
//! let cs = pins.gpio13.to_digital_output(true);
//!
//! // Can now pass the "spi" and "cs" objects into any `embedded-hal` device
//! // driver that works with a SPI interface and a chip select signal.
//! let dev = any_driver::Driver::new(spi, cs);
//! ```
//!
//! There are more examples in the `examples` directory within the crate source
//! code. The above example is a cut-down version of the `ssd1322` example.

#![no_std]
#![feature(never_type)]
#![feature(optin_builtin_traits)]

pub extern crate lpc81x_pac as lpc81x;
pub use lpc81x::Interrupt;
pub use lpc81x::NVIC_PRIO_BITS;

pub mod i2c;
pub mod pinint;
pub mod pins;
pub mod spi;

/// Singleton container for the peripherals modeled by this HAL crate.
///
/// All of the functionality of this library begins in the `Peripherals`
/// singleton. The initial content represents the state of the system and
/// peripherals at reset (after the built-in bootloader launches the user
/// program) and these objects can be moved elsewhere to configure the
/// peripherals for a particular application.
///
/// All of the main interface peripherals take pin objects as arguments.
/// Unassigned pins start off in the `pins` field of `Peripheraps`, and can
/// be moved elsewhere to assign them to functions. This interface ensures
/// at compile time that two outputs cannot be sharing the same pin, which is
/// forbidden by the LPC81x "switch matrix" peripheral.
pub struct Peripherals {
    /// The main accessors for the device pins.
    pub pins: pins::Pins,

    /// Alternative accessors for the device pins' digital inputs.
    ///
    /// This is an alternative to `pins` that provides access only to the input
    /// parts, and crucially allows access to the input parts even when
    /// ownership of the members of `pins` have been transferred elsewhere.
    pub pin_inputs: pins::PinInputs,

    pub pin_interrupts: pinint::Inactive,

    /// The first SPI peripheral, initially inactive.
    pub spi0: spi::SPI0<
        spi::mode::Inactive,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
    >,

    /// The second SPI peripheral, initially inactive.
    ///
    /// This device is only present in models LPC812M101JDH16 and
    /// LPC812M101JDH20 (TSSOP packages).
    pub spi1: spi::SPI1<
        spi::mode::Inactive,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
        pins::mode::Unassigned,
    >,

    // The I2C peripheral, initially inactive.
    pub i2c: i2c::I2C<
        pins::mode::Unassigned,
        pins::mode::Unassigned,
        i2c::mode::Host<i2c::mode::Inactive>,
        i2c::mode::Device<i2c::mode::Inactive>,
        i2c::mode::Monitor<i2c::mode::Inactive>,
    >,
}

impl Peripherals {
    fn new() -> Self {
        Self {
            pins: pins::Pins::new(),
            pin_inputs: pins::PinInputs::new(),
            pin_interrupts: pinint::Inactive::new(),
            spi0: spi::SPI0::new(),
            spi1: spi::SPI1::new(),
            i2c: i2c::I2C::new(),
        }
    }

    // Can be called only from methods on objects that are only accessible
    // (directly or indirectly) from a Peripherals instance.
    fn pac() -> lpc81x::Peripherals {
        unsafe { lpc81x::Peripherals::steal() }
    }

    /// Obtain (only once) the singleton Peripherals object.
    ///
    /// On subsequent calls, returns `None`.
    pub fn take() -> Option<Self> {
        // Here we rely on the singleton implementation of the underlying
        // PAC Peripherals::take to make this safe. It temporarily disables
        // interrupts while managing its own static mut variable to track
        // whether the singleton was already taken.
        match lpc81x::Peripherals::take() {
            Some(_) => Some(Self::new()),
            None => None,
        }
    }

    pub unsafe fn steal() -> Self {
        Self::new()
    }

    /// Returns a value representing the specific LPC8xx model of the current
    /// device, allowing dynamic inspection of some details that vary by model.
    pub fn model(&self) -> Model {
        let syscon = lpc81x_pac::SYSCON::ptr();
        let raw = unsafe { (*syscon).device_id.read().deviceid().bits() };
        match raw {
            0x00008100 => Model::LPC810M021FN8,
            0x00008110 => Model::LPC811M001JDH16,
            0x00008120 => Model::LPC812M101JDH16,
            0x00008121 => Model::LPC812M101JD20,
            0x00008122 => Model::LPC812M101JDH20,
            0x00008123 => Model::LPC812M101JTB16,
            _ => panic!("unsupported model"),
        }
    }

    /// Consumes the HAL-level peripherals to unwrap the PAC-level
    /// peripherhals.
    pub fn release_pac(self) -> lpc81x::Peripherals {
        unsafe { self.steal_pac() }
    }

    /// Returns the PAC-level peripherals while leaving the caller with ownership
    /// of the HAL-level peripherals too.
    ///
    /// This is unsafe because the raw PAC peripherals API may be used to
    /// reconfigure the hardware in a way that the HAL isn't aware of.
    pub unsafe fn steal_pac(&self) -> lpc81x::Peripherals {
        Self::pac()
    }
}

/// Identifies a particular model of LPC8xx device.
pub enum Model {
    LPC810M021FN8,
    LPC811M001JDH16,
    LPC812M101JDH16,
    LPC812M101JD20,
    LPC812M101JDH20,
    LPC812M101JTB16,
}

impl Model {
    /// Returns true if the given pin is available for this model.
    ///
    /// This library does not prevent using pins that are not available on
    /// a particular device model or package, but an application intended to
    /// be portable can use this to adapt to different pin subsets.
    pub fn has_pin<P: pins::Pin>(&self, pin: &P) -> bool {
        unused(pin);
        match self {
            Model::LPC810M021FN8 => P::NUMBER <= 5,
            _ => true,
        }
    }

    /// Returns true if the second SPI device (SPI1) is available for this
    /// model.
    ///
    /// This library does not prevent using SPI1 on devices where it is
    /// unavailable. An application intended to be portable can use this to
    /// detect when SPI1 is unavailable and perform some sort of graceful
    /// fallback, such as using a "bit-bang" SPI implementation.
    pub fn has_spi1(&self) -> bool {
        match self {
            Model::LPC812M101JDH16 => true,
            Model::LPC812M101JDH20 => true,
            _ => false,
        }
    }
}

#[inline(always)]
fn unused<T>(_v: T) {}