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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/***********************************************************************************************************************
 * Copyright (c) 2019 by the authors
 *
 * Author: André Borrmann
 * License: Apache License 2.0
 **********************************************************************************************************************/
#![doc(html_root_url = "https://docs.rs/ruspiro-i2c/0.3.1")]
#![cfg_attr(not(any(test, doctest)), no_std)]

//! # Raspberry Pi I²C bus interface
//!
//! Simple access to the I²C bus available on the Raspberry Pi. When the I²C bus is used this reserves the GPIO pins 2
//! and 3 for exclusive use by the bus.
//!
//! # Usage
//!
//! ```no_run
//! # use ruspiro_i2c::I2C;
//! # fn doc() {
//!     I2C.take_for(|i2c| {
//!         if i2c.initialize(250_000_000, true).is_ok() {
//!             println!("scan I2C devices connected to RPi");
//!             let devices = i2c.scan().unwrap();
//!             for d in devices {
//!                 println!("device detected at 0x{:2X}", d);
//!             }
//!         }
//!     });
//! # }
//! ```
//!
//! To work with a device connected to the I²C bus it's recommended to first check whether this is
//! connected at the specific address. This could be done like so:
//! ```no_run
//! # use ruspiro_i2c::I2C;
//! # fn doc() {
//!     let device_addr = 0x68;
//!     // check if device is connected
//!     I2C.take_for(|i2c| {
//!         if i2c.check_device(device_addr).is_ok() {
//!             // now that we know the device exists and is connected to something with it
//!         }
//!     });
//! # }
//! ```
//! Once done simple use the funtions to write to or read from the device registers as required.
//! 
//! # Features
//!
//! - ``ruspiro_pi3`` is active by default and ensures the proper MMIO base address is used for Raspberry Pi 3
//!

extern crate alloc;
use alloc::vec::Vec;
use ruspiro_register::*;
use ruspiro_singleton::Singleton;

mod interface;

/// Static singleton accessor for the I²C bus peripheral
/// To use the contained i2c API in a safe way use the ``take_for``
/// function passing a clousure that can safely use the resource
/// ```no_run
/// # use ruspiro_i2c::*;
/// # fn doc() {
/// I2C.take_for(|i2c| {
///     // safe access here e.g. to initialize
///     i2c.initialize(250_000_000, true).unwrap();
/// });
/// # }
/// ```
pub static I2C: Singleton<I2cImpl> = Singleton::new(I2cImpl::new());

/// I²C peripheral representation
pub struct I2cImpl {
    initialized: bool,
}

pub type I2cResult<T> = Result<T, &'static str>;

impl I2cImpl {
    /// create a new instance of the I2c implementation. This will only be used to
    /// prepare the static singleton I²C accessor.
    pub(crate) const fn new() -> Self {
        I2cImpl { initialized: false }
    }

    /// Initialize the I²C bus for further usage. This will require the GPIO pins 2 and 3 to be available for usage.
    /// If they have been already occupied before this initialization is called an error will be returned.
    pub fn initialize(&mut self, core_speed: u32, fast_mode: bool) -> I2cResult<()> {
        if !self.initialized {
            interface::initialize(core_speed, fast_mode).and_then(|_| {
                self.initialized = true;
                Ok(())
            })
        } else {
            Ok(())
        }
    }

    /// Scan for I²C devices currently connected to the I²C bus.
    /// The scan will just try to get an acknowledge message from any slave address between
    /// 0x00 and 0x7F. If a device is connected this call succeeds/get's acknowledged and the
    /// corresponding address is put into the returned vector.
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     let devices = I2C.take_for(|i2c| i2c.scan()).unwrap();
    ///     for d in devices {
    ///         println!("Device at address: 0x{:X}", d);
    ///     }
    /// # }
    pub fn scan(&self) -> I2cResult<Vec<u8>> {
        self.is_initializied()?;
        Ok(interface::scan_devices())
    }

    /// Checks if a device with the given address is connected to the I²C bus.
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     if I2C.take_for(|i2c| i2c.check_device(0x68)).is_ok() {
    ///         println!("device at 0x68 connected");
    ///     }
    /// # }
    /// ```
    pub fn check_device(&self, addr: u8) -> I2cResult<()> {
        self.is_initializied()?;
        interface::check_device(addr)
    }

    /// Read a u8 from a device register
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     let value = I2C.take_for(|i2c| i2c.read_register_u8(0x68, 0x20)).unwrap();
    /// # }
    /// ```
    pub fn read_register_u8(&self, device_addr: u8, reg: u8) -> I2cResult<u8> {
        self.is_initializied()?;
        interface::read_reg_u8(device_addr, reg)
    }

    /// Read a u16 from a device register.
    /// As usually all I²C register are 8 Bit wide this will only return a valid value
    /// if the device supports auto-increment of the actual register while reading
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     // read_register_u16 will actually read the registers 0x20 and 0x21 and combine
    ///     // both u8 values into the u16 return value.
    ///     let value = I2C.take_for(|i2c| i2c.read_register_u16(0x68, 0x20)).unwrap();
    /// # }
    /// ```
    pub fn read_register_u16(&self, device_addr: u8, reg: u8) -> I2cResult<u16> {
        self.is_initializied()?;
        interface::read_reg_u16(device_addr, reg)
    }

    /// Read a u8 array from a device register.
    /// As usually all I²C register are 8 Bit wide this will only return a valid value
    /// if the device supports auto-increment of the actual register while reading
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     let mut buffer: [u8; 4] = [0; 4];
    ///     // the buffer read will actuall read the registers 0x20, 0x21, 0x22, 0x23
    ///     // and put the data into the byte buffer given (if register auto increment is supported
    ///     // by this device)
    ///     let _ = I2C.take_for(|i2c| i2c.read_register_buff(0x68, 0x20, &mut buffer)).unwrap();
    /// # }
    /// ```
    pub fn read_register_buff(
        &self,
        device_addr: u8,
        reg: u8,
        buffer: &mut [u8],
    ) -> I2cResult<usize> {
        self.is_initializied()?;
        interface::read_reg_data(device_addr, reg, buffer)
    }

    /// Read a specific field from a 8 Bit device register.
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # use ruspiro_register::*;
    /// # fn doc() {
    ///     // define an arbitrary register field with 1 bit size at offset 2
    ///     let field = RegisterField::<u8>::new(1, 2);
    ///     let field_value = I2C.take_for(|i2c| i2c.read_register_field(0x68, 0x20, field)).unwrap();
    /// # }
    /// ```
    pub fn read_register_field(
        &self,
        device_addr: u8,
        reg: u8,
        field: RegisterField<u8>,
    ) -> I2cResult<RegisterFieldValue<u8>> {
        self.is_initializied()?;
        let value = interface::read_reg_u8(device_addr, reg)?;
        Ok(RegisterFieldValue::<u8>::new(field, value >> field.shift()))
    }

    /// Write u8 data to a device without specifying a register.
    /// This is helpful for devices that may not provide any registers or have only one register
    /// to wrte to. In those cases the device accepts the data without specifying the register in
    /// the first place.
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     I2C.take_for(|i2c| i2c.write_u8(0x68, 12)).unwrap();
    /// # }
    /// ```
    pub fn write_u8(&self, device_addr: u8, data: u8) -> I2cResult<()> {
        self.is_initializied()?;
        interface::write_raw_u8(device_addr, data)
    }

    /// Write u8 data to a device register
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     I2C.take_for(|i2c| i2c.write_register_u8(0x68, 0x20, 12)).unwrap();
    /// # }
    /// ```
    pub fn write_register_u8(&self, device_addr: u8, reg: u8, data: u8) -> I2cResult<()> {
        self.is_initializied()?;
        interface::write_reg_u8(device_addr, reg, data)
    }

    /// Write u16 data to a device register.
    /// As usually all I²C register are 8 Bit wide this will only properly write the value
    /// if the device supports auto-increment of the actual register while reading
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     // this will actually write 0x12 to register 0x20 and 0xab to register 0x21
    ///     // if the device supports auto increment of registers for writes
    ///     I2C.take_for(|i2c| i2c.write_register_u16(0x68, 0x20, 0x12ab)).unwrap();
    /// # }
    /// ```
    pub fn write_register_u16(&self, device_addr: u8, reg: u8, data: u16) -> I2cResult<()> {
        self.is_initializied()?;
        interface::write_reg_u16(device_addr, reg, data)
    }

    /// Write a u8 array to a device register.
    /// As usually all I²C register are 8 Bit wide this will only properly write the value
    /// if the device supports auto-increment of the actual register while reading
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # fn doc() {
    ///     let data: [u8; 3] = [0, 1, 2];
    ///     I2C.take_for(|i2c| i2c.write_register_buff(0x68, 0x20, &data)).unwrap();
    /// # }
    /// ```
    pub fn write_register_buff(&self, device_addr: u8, reg: u8, data: &[u8]) -> I2cResult<()> {
        self.is_initializied()?;
        interface::write_reg_data(device_addr, reg, data)
    }

    /// Write a specific register field to a 8 Bit device register.
    /// # Example
    /// ```no_run
    /// # use ruspiro_i2c::*;
    /// # use ruspiro_register::*;
    /// # fn doc() {
    ///     // define an arbitrary field with bit size 2 and offset 3
    ///     let field = RegisterField::<u8>::new(2, 3);
    ///     // define the field value
    ///     let field_value = RegisterFieldValue::<u8>::new(field, 0b10);
    ///     let value = I2C.take_for(|i2c| i2c.write_register_field(0x68, 0x20, field_value)).unwrap();
    /// # }
    /// ```
    pub fn write_register_field(
        &self,
        device_addr: u8,
        reg: u8,
        value: RegisterFieldValue<u8>,
    ) -> I2cResult<()> {
        self.is_initializied()?;
        let old_value = self.read_register_u8(device_addr, reg)?;
        let new_value = (old_value & !value.mask()) | value.raw_value();
        interface::write_reg_u8(device_addr, reg, new_value)
    }

    #[inline(always)]
    fn is_initializied(&self) -> I2cResult<()> {
        if !self.initialized {
            Err("I2C Bus not initialized")
        } else {
            Ok(())
        }
    }
}