Skip to main content

pamoja_gpio/
i2c.rs

1//! I2C device addressing per the NXP I2C-bus specification (UM10204).
2//!
3//! An I2C transfer begins with the controller sending the device's address. The exact
4//! bytes are pinned down by the specification, and they are easy to get subtly wrong: the
5//! 7-bit address shares its byte with the read/write bit, so the value a datasheet prints
6//! is not the byte that goes on the wire, and the 10-bit extension spends a reserved
7//! prefix and spreads its bits across two bytes. This module builds those bytes exactly
8//! and rejects an address that is out of range or, on request, one the specification
9//! reserves.
10
11use crate::GpioError;
12
13/// The lowest 7-bit address the specification keeps for itself.
14///
15/// Addresses from here up are reserved: `0x78` to `0x7B` introduce a 10-bit address and
16/// the rest are held back, so a part answering in this range is a bus feature rather than
17/// a device a driver talks to. [`Address::is_reserved`] covers this range and
18/// [`RESERVED_BELOW`].
19pub const RESERVED_FROM: u8 = 0x78;
20
21/// The first 7-bit address above the reserved block at the bottom of the range.
22///
23/// Everything below this, `0x00` to `0x07`, is reserved: `0x00` is the general call every
24/// device listens to, and the rest carry bus functions.
25pub const RESERVED_BELOW: u8 = 0x08;
26
27/// Largest valid 7-bit address (inclusive).
28const MAX_SEVEN_BIT: u16 = 0x7F;
29/// Largest valid 10-bit address (inclusive).
30const MAX_TEN_BIT: u16 = 0x3FF;
31/// The five-bit `11110` prefix (in the top of the first byte) that marks a 10-bit address.
32const TEN_BIT_PREFIX: u8 = 0xF0;
33
34/// Whether an I2C transfer reads from or writes to the device.
35///
36/// The direction rides in the least-significant bit of the address byte: `0` for a write,
37/// `1` for a read.
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum Direction {
40    /// The controller writes to the device. R/W bit `0`.
41    Write,
42    /// The controller reads from the device. R/W bit `1`.
43    Read,
44}
45
46impl Direction {
47    /// Returns the R/W bit this direction places in the low bit of the address byte.
48    ///
49    /// # Returns
50    ///
51    /// `0` for [`Write`](Direction::Write), `1` for [`Read`](Direction::Read).
52    pub fn rw_bit(self) -> u8 {
53        match self {
54            Direction::Write => 0,
55            Direction::Read => 1,
56        }
57    }
58}
59
60/// An I2C device address, 7-bit or 10-bit, validated to its range.
61///
62/// I2C addresses come in two widths. The original 7-bit address shares its byte with the
63/// R/W bit, so it lands on the wire as `(address << 1) | r/w`. The later 10-bit extension
64/// stays backward compatible by spending the reserved `11110xx` prefix: the first byte is
65/// `11110`, then the top two address bits, then the R/W bit, and the second byte is the
66/// low eight address bits. Construct an address with [`seven_bit`](Address::seven_bit) or
67/// [`ten_bit`](Address::ten_bit), which reject out-of-range values, then turn it into the
68/// bytes a controller sends with [`write_frame`](Address::write_frame).
69///
70/// # Examples
71///
72/// ```
73/// use pamoja_gpio::i2c::{Address, Direction};
74///
75/// // 7-bit: a BME280 at 0x76 writes as 0xEC and reads as 0xED.
76/// let bme = Address::seven_bit(0x76)?;
77/// let mut buf = [0u8; 2];
78/// assert_eq!(bme.write_frame(Direction::Write, &mut buf)?, 1);
79/// assert_eq!(buf[0], 0xEC);
80/// assert_eq!(bme.write_frame(Direction::Read, &mut buf)?, 1);
81/// assert_eq!(buf[0], 0xED);
82/// # Ok::<(), pamoja_gpio::GpioError>(())
83/// ```
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct Address {
86    value: u16,
87    ten_bit: bool,
88}
89
90impl Address {
91    /// Creates a 7-bit I2C address.
92    ///
93    /// The whole range is accepted, including the addresses the specification reserves;
94    /// those are still legal on the wire (the general call address `0x00` is a broadcast,
95    /// for instance). Use [`is_reserved`](Address::is_reserved) to test for them.
96    ///
97    /// # Arguments
98    ///
99    /// * `address` - the 7-bit device address, `0x00..=0x7F`.
100    ///
101    /// # Returns
102    ///
103    /// The validated address.
104    ///
105    /// # Errors
106    ///
107    /// [`GpioError::AddressOutOfRange`] if `address` exceeds `0x7F`.
108    pub fn seven_bit(address: u8) -> Result<Address, GpioError> {
109        if address as u16 > MAX_SEVEN_BIT {
110            return Err(GpioError::AddressOutOfRange);
111        }
112        Ok(Address {
113            value: address as u16,
114            ten_bit: false,
115        })
116    }
117
118    /// Creates a 10-bit I2C address.
119    ///
120    /// # Arguments
121    ///
122    /// * `address` - the 10-bit device address, `0x000..=0x3FF`.
123    ///
124    /// # Returns
125    ///
126    /// The validated address.
127    ///
128    /// # Errors
129    ///
130    /// [`GpioError::AddressOutOfRange`] if `address` exceeds `0x3FF`.
131    pub fn ten_bit(address: u16) -> Result<Address, GpioError> {
132        if address > MAX_TEN_BIT {
133            return Err(GpioError::AddressOutOfRange);
134        }
135        Ok(Address {
136            value: address,
137            ten_bit: true,
138        })
139    }
140
141    /// Returns the address value, without the R/W bit.
142    ///
143    /// # Returns
144    ///
145    /// The 7- or 10-bit address as passed to the constructor.
146    pub fn value(self) -> u16 {
147        self.value
148    }
149
150    /// Returns `true` if this is a 10-bit address.
151    pub fn is_ten_bit(self) -> bool {
152        self.ten_bit
153    }
154
155    /// Returns the number of bytes [`write_frame`](Address::write_frame) emits.
156    ///
157    /// # Returns
158    ///
159    /// `1` for a 7-bit address, `2` for a 10-bit address.
160    pub fn frame_len(self) -> usize {
161        if self.ten_bit {
162            2
163        } else {
164            1
165        }
166    }
167
168    /// Returns `true` if a 7-bit address falls in a range the I2C specification reserves.
169    ///
170    /// UM10204 reserves `0x00..=0x07` (general call and START byte, CBUS, a bus-format
171    /// code, a future code, and the Hs-mode master codes) and `0x78..=0x7F` (the 10-bit
172    /// addressing prefix and the device-ID codes), leaving `0x08..=0x77` for ordinary
173    /// devices. A 10-bit address is not reserved in this sense, so this returns `false`
174    /// for one.
175    ///
176    /// # Returns
177    ///
178    /// `true` if this is a 7-bit address in `0x00..=0x07` or `0x78..=0x7F`.
179    pub fn is_reserved(self) -> bool {
180        !self.ten_bit
181            && (self.value < u16::from(RESERVED_BELOW) || self.value >= u16::from(RESERVED_FROM))
182    }
183
184    /// Returns `true` if this is the general call address `0x00`, the broadcast every
185    /// device on the bus listens to.
186    pub fn is_general_call(self) -> bool {
187        !self.ten_bit && self.value == 0x00
188    }
189
190    /// Returns the addressing frame this address puts on the bus.
191    ///
192    /// The same bytes [`write_frame`](Address::write_frame) produces, as a value, so a
193    /// caller does not have to size and pass a scratch buffer to find out what an address
194    /// looks like on the wire.
195    ///
196    /// # Arguments
197    ///
198    /// * `direction` - whether the transfer reads or writes, which sets the R/W bit.
199    ///
200    /// # Returns
201    ///
202    /// The frame, one byte for a 7-bit address and two for a 10-bit one.
203    pub fn frame(self, direction: Direction) -> AddressFrame {
204        let mut bytes = [0u8; 2];
205        let len = self.write_frame(direction, &mut bytes).unwrap_or_default();
206        AddressFrame { bytes, len }
207    }
208
209    /// Writes the address byte(s) a controller puts on the bus for a transfer.
210    ///
211    /// For a 7-bit address this is the single byte `(address << 1) | r/w`. For a 10-bit
212    /// address it is two bytes: `11110` then the top two address bits then the R/W bit,
213    /// followed by the low eight address bits. A 10-bit read in practice first addresses
214    /// the device with a write frame and then, after a repeated START, re-sends this first
215    /// byte with the read bit set; this method emits the bytes for the `direction` asked
216    /// for, leaving the START/repeated-START sequencing to the driver.
217    ///
218    /// # Arguments
219    ///
220    /// * `direction` - whether the transfer reads or writes, which sets the R/W bit.
221    /// * `out` - the buffer the frame is written into; it must hold at least
222    ///   [`frame_len`](Address::frame_len) bytes.
223    ///
224    /// # Returns
225    ///
226    /// The number of bytes written: `1` for a 7-bit address, `2` for a 10-bit address.
227    ///
228    /// # Errors
229    ///
230    /// [`GpioError::BufferTooSmall`] if `out` is shorter than [`frame_len`](Address::frame_len).
231    pub fn write_frame(self, direction: Direction, out: &mut [u8]) -> Result<usize, GpioError> {
232        let rw = direction.rw_bit();
233        if self.ten_bit {
234            if out.len() < 2 {
235                return Err(GpioError::BufferTooSmall);
236            }
237            let high = ((self.value >> 8) as u8) & 0x03;
238            out[0] = TEN_BIT_PREFIX | (high << 1) | rw;
239            out[1] = (self.value & 0xFF) as u8;
240            Ok(2)
241        } else {
242            if out.is_empty() {
243                return Err(GpioError::BufferTooSmall);
244            }
245            out[0] = ((self.value as u8) << 1) | rw;
246            Ok(1)
247        }
248    }
249}
250
251/// The bytes an [`Address`] puts on the bus to address a device.
252///
253/// One byte for a 7-bit address and two for a 10-bit one, held inline so producing one
254/// allocates nothing and needs no caller-supplied buffer.
255#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256pub struct AddressFrame {
257    bytes: [u8; 2],
258    len: usize,
259}
260
261impl AddressFrame {
262    /// Returns the frame bytes, in the order they go on the bus.
263    ///
264    /// # Returns
265    ///
266    /// One byte for a 7-bit address, two for a 10-bit one.
267    pub fn as_bytes(&self) -> &[u8] {
268        &self.bytes[..self.len]
269    }
270
271    /// Returns how many bytes the frame occupies.
272    ///
273    /// # Returns
274    ///
275    /// `1` for a 7-bit address, `2` for a 10-bit one.
276    pub fn len(&self) -> usize {
277        self.len
278    }
279
280    /// Returns whether the frame is empty, which it never is.
281    ///
282    /// # Returns
283    ///
284    /// Always `false`; an address always addresses something.
285    pub fn is_empty(&self) -> bool {
286        self.len == 0
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    /// Builds the frame and asserts it equals the expected bytes on the wire.
295    fn assert_frame(address: Address, direction: Direction, expected: &[u8]) {
296        let mut buf = [0u8; 2];
297        let n = address.write_frame(direction, &mut buf).unwrap();
298        assert_eq!(&buf[..n], expected);
299    }
300
301    #[test]
302    fn seven_bit_frames_match_known_device_bytes() {
303        // Reference bytes printed on real datasheets, where the 7-bit address shifts up
304        // one and the R/W bit fills the low bit.
305        // DS3231 RTC / MPU-6050 at 0x68.
306        assert_frame(Address::seven_bit(0x68).unwrap(), Direction::Write, &[0xD0]);
307        assert_frame(Address::seven_bit(0x68).unwrap(), Direction::Read, &[0xD1]);
308        // SSD1306 OLED at 0x3C.
309        assert_frame(Address::seven_bit(0x3C).unwrap(), Direction::Write, &[0x78]);
310        assert_frame(Address::seven_bit(0x3C).unwrap(), Direction::Read, &[0x79]);
311        // AT24C32 EEPROM / PCF8574 at 0x50.
312        assert_frame(Address::seven_bit(0x50).unwrap(), Direction::Write, &[0xA0]);
313        assert_frame(Address::seven_bit(0x50).unwrap(), Direction::Read, &[0xA1]);
314    }
315
316    #[test]
317    fn ten_bit_frame_matches_spec_worked_example() {
318        // UM10204's worked 10-bit example, address 0x2A5 = 0b10_1010_0101:
319        // first byte 11110 10 0 = 0xF4, second byte 1010_0101 = 0xA5.
320        let addr = Address::ten_bit(0x2A5).unwrap();
321        assert_frame(addr, Direction::Write, &[0xF4, 0xA5]);
322        assert_frame(addr, Direction::Read, &[0xF5, 0xA5]);
323    }
324
325    #[test]
326    fn ten_bit_frame_at_the_range_bounds() {
327        // 0x000: prefix only, low byte zero.
328        assert_frame(
329            Address::ten_bit(0x000).unwrap(),
330            Direction::Write,
331            &[0xF0, 0x00],
332        );
333        // 0x3FF: top two bits set (11110 11 r/w), low byte all ones.
334        assert_frame(
335            Address::ten_bit(0x3FF).unwrap(),
336            Direction::Write,
337            &[0xF6, 0xFF],
338        );
339        assert_frame(
340            Address::ten_bit(0x3FF).unwrap(),
341            Direction::Read,
342            &[0xF7, 0xFF],
343        );
344    }
345
346    #[test]
347    fn out_of_range_addresses_are_rejected() {
348        assert_eq!(Address::seven_bit(0x80), Err(GpioError::AddressOutOfRange));
349        assert_eq!(Address::ten_bit(0x400), Err(GpioError::AddressOutOfRange));
350        // The top of each range is accepted.
351        assert!(Address::seven_bit(0x7F).is_ok());
352        assert!(Address::ten_bit(0x3FF).is_ok());
353    }
354
355    #[test]
356    fn reserved_ranges_match_the_spec() {
357        // Reserved: 0x00..=0x07 and 0x78..=0x7F.
358        for addr in (0x00..=0x07).chain(0x78..=0x7F) {
359            assert!(
360                Address::seven_bit(addr).unwrap().is_reserved(),
361                "{addr:#04x}"
362            );
363        }
364        // The usable range is everything between.
365        for addr in 0x08..=0x77 {
366            assert!(
367                !Address::seven_bit(addr).unwrap().is_reserved(),
368                "{addr:#04x}"
369            );
370        }
371        // A 10-bit address is never reserved in the 7-bit sense.
372        assert!(!Address::ten_bit(0x002).unwrap().is_reserved());
373    }
374
375    #[test]
376    fn general_call_is_address_zero() {
377        assert!(Address::seven_bit(0x00).unwrap().is_general_call());
378        assert!(!Address::seven_bit(0x01).unwrap().is_general_call());
379        assert!(!Address::ten_bit(0x000).unwrap().is_general_call());
380    }
381
382    #[test]
383    fn frame_len_and_too_small_buffers() {
384        assert_eq!(Address::seven_bit(0x40).unwrap().frame_len(), 1);
385        assert_eq!(Address::ten_bit(0x100).unwrap().frame_len(), 2);
386
387        let mut empty = [];
388        assert_eq!(
389            Address::seven_bit(0x40)
390                .unwrap()
391                .write_frame(Direction::Write, &mut empty),
392            Err(GpioError::BufferTooSmall)
393        );
394        let mut one = [0u8; 1];
395        assert_eq!(
396            Address::ten_bit(0x100)
397                .unwrap()
398                .write_frame(Direction::Write, &mut one),
399            Err(GpioError::BufferTooSmall)
400        );
401    }
402}