pamoja_gpio/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! On-board bus addressing and pin logic for the pamoja SDK.
4//!
5//! Before a node reaches any network it has to talk to the chips wired to the board it
6//! sits on. Three interfaces cover almost everything cheap hardware uses: I2C for the
7//! dense breakout sensors (a BME280, an INA226, an MPU9250, an SSD1306 screen), SPI for
8//! displays, SD cards, and LoRa radios, and plain GPIO pins for the relays, valves,
9//! buttons, and motion sensors that switch a single line. Each carries a small, exact
10//! piece of logic that is pure arithmetic with no hardware attached, and getting it wrong
11//! is a classic field bug: the wrong I2C address byte, the wrong SPI clock mode, or an
12//! active-low relay driven as if it were active-high.
13//!
14//! This crate is that logic, with no pins toggled and no allocation:
15//!
16//! - [`i2c`] - I2C addressing per the NXP I2C-bus specification (UM10204): the 7-bit
17//! address byte `(address << 1) | r/w`, the two-byte `11110xx` frame for a 10-bit
18//! address, and the reserved ranges (`0x00..=0x07` and `0x78..=0x7F`) that leave
19//! `0x08..=0x77` for real devices, so a bad address is caught before it reaches the bus.
20//! - [`spi`] - the four SPI clock [`Mode`](spi::Mode)s as the `(CPOL, CPHA)` pair every
21//! datasheet quotes, plus bit order, so "mode 3, MSB first" is a checked value rather
22//! than two booleans a caller can transpose.
23//! - [`pin`] - the GPIO pin model: physical [`Level`](pin::Level), input pull and output
24//! drive, the interrupt [`Edge`](pin::Edge), and an active-high/active-low
25//! [`Polarity`](pin::Polarity) that maps a logical "asserted" onto the physical level,
26//! so an active-low button or relay is handled by the type rather than by remembering
27//! to invert.
28//!
29//! Everything is exact integer work over `Copy` values, so the same logic runs on the
30//! smallest microcontroller driving the bus. Clocking the bytes and toggling the lines
31//! themselves is the job of [`pamoja-hal`](https://docs.rs/pamoja-hal), whose
32//! `embedded-hal` traits every board implements. The [`switch`] module (the
33//! `embedded-hal` feature, on by default) puts the two together for the parts that are
34//! a single line: [`Switch`](switch::Switch) drives a relay, a valve, or a lamp as a
35//! core `Actuator`, and [`Contact`](switch::Contact) reads a button, a float switch,
36//! or a motion detector as a core `Sensor`, each with its [`Polarity`](pin::Polarity)
37//! carried by the type rather than remembered at every call. The `linux` module (the
38//! `linux` feature) opens a line on a Linux board through the GPIO character device, so the
39//! same switch runs a real relay on a Raspberry Pi.
40//!
41//! # Examples
42//!
43//! ```
44//! use pamoja_gpio::i2c::{Address, Direction};
45//! use pamoja_gpio::pin::{Level, Polarity};
46//! use pamoja_gpio::spi::Mode;
47//!
48//! // A DS3231 real-time clock answers at 7-bit address 0x68. Its address frame is one
49//! // byte: the address, then the bit that says whether the controller reads or writes.
50//! let rtc = Address::seven_bit(0x68)?;
51//! let read = rtc.frame(Direction::Read);
52//! assert_eq!(read.as_bytes(), [(0x68 << 1) | Direction::Read.rw_bit()]);
53//!
54//! // SPI clock mode 0 is the (CPOL, CPHA) pair (false, false), as a datasheet quotes it.
55//! assert_eq!(Mode::Mode0.cpol_cpha(), (false, false));
56//!
57//! // An active-low relay is energized by driving its pin low.
58//! assert_eq!(Polarity::ActiveLow.level(true), Level::Low);
59//! # Ok::<(), pamoja_gpio::GpioError>(())
60//! ```
61
62#[cfg(feature = "embedded-hal")]
63extern crate alloc;
64
65pub mod i2c;
66#[cfg(feature = "linux")]
67pub mod linux;
68pub mod pin;
69pub mod spi;
70#[cfg(feature = "embedded-hal")]
71pub mod switch;
72
73mod error;
74
75pub use error::GpioError;