pamoja_sensors/driver.rs
1//! What the drivers share: how a register-mapped part is reached over a bus.
2//!
3//! Bosch's BME280 and BMP280 speak one register protocol over two buses, I2C and SPI.
4//! [`RegisterBus`] names the two operations that protocol needs, a multi-byte read
5//! starting at a register and a single-byte write, and [`I2cRegisters`] and
6//! [`SpiRegisters`] implement them: over I2C the register address is written and the
7//! data read back in one transaction, over SPI bit 7 of the control byte is the
8//! read/write flag and the address increments on its own, as the BME280 datasheet's
9//! interface chapter lays out. A driver written against the trait runs on either bus.
10//!
11//! Texas Instruments' parts keep 16-bit registers that travel most significant byte
12//! first over I2C; [`read_word`] and [`write_word`] are that shape.
13
14use embedded_hal::i2c::I2c;
15use embedded_hal::spi::{Operation, SpiDevice};
16
17/// The read/write flag in a Bosch SPI control byte: set to read, clear to write.
18pub const SPI_READ: u8 = 0x80;
19
20/// The mask that leaves the seven address bits of a Bosch SPI control byte.
21pub const SPI_ADDRESS_MASK: u8 = 0x7F;
22
23/// A part's register map reached over some bus.
24///
25/// # Examples
26///
27/// ```
28/// use pamoja_hal::script::{I2cScript, I2cStep};
29/// use pamoja_sensors::bme280::{register, CHIP_ID, I2C_ADDRESS_PRIMARY, RESET_WORD};
30/// use pamoja_sensors::driver::{I2cRegisters, RegisterBus};
31///
32/// // A BME280 answers its chip id, then takes the word that resets it.
33/// const PART: u8 = I2C_ADDRESS_PRIMARY;
34/// let script = I2cScript::new([
35/// I2cStep::write_read(PART, [register::CHIP_ID], [CHIP_ID]),
36/// I2cStep::write(PART, [register::RESET, RESET_WORD]),
37/// ]);
38/// let mut part = I2cRegisters::new(script, PART);
39/// assert_eq!(part.read_register(register::CHIP_ID)?, CHIP_ID);
40/// part.write_register(register::RESET, RESET_WORD)?;
41/// assert!(part.release().done());
42/// # Ok::<(), pamoja_hal::script::ScriptError>(())
43/// ```
44pub trait RegisterBus {
45 /// The error the bus underneath reports.
46 type Error;
47
48 /// Reads consecutive registers starting at `first`, as many as `buffer` holds.
49 ///
50 /// # Arguments
51 ///
52 /// * `first` - the address of the first register.
53 /// * `buffer` - where the register values go, one byte per register.
54 ///
55 /// # Errors
56 ///
57 /// Returns the bus error if the part does not answer.
58 fn read_registers(&mut self, first: u8, buffer: &mut [u8]) -> Result<(), Self::Error>;
59
60 /// Writes one register.
61 ///
62 /// # Arguments
63 ///
64 /// * `register` - the register address.
65 /// * `value` - the byte to write.
66 ///
67 /// # Errors
68 ///
69 /// Returns the bus error if the part does not answer.
70 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Self::Error>;
71
72 /// Reads one register.
73 ///
74 /// # Arguments
75 ///
76 /// * `register` - the register address.
77 ///
78 /// # Returns
79 ///
80 /// The register's byte.
81 ///
82 /// # Errors
83 ///
84 /// Returns the bus error if the part does not answer.
85 fn read_register(&mut self, register: u8) -> Result<u8, Self::Error> {
86 let mut byte = [0u8; 1];
87 self.read_registers(register, &mut byte)?;
88 Ok(byte[0])
89 }
90}
91
92/// A register map behind an I2C address.
93///
94/// A read writes the register address and reads the values back in one transaction,
95/// with a repeated start between them; a write sends the address and the value.
96#[derive(Debug)]
97pub struct I2cRegisters<I2C> {
98 bus: I2C,
99 address: u8,
100}
101
102impl<I2C> I2cRegisters<I2C> {
103 /// Wraps an I2C bus and the part's 7-bit address.
104 ///
105 /// # Arguments
106 ///
107 /// * `bus` - the I2C bus the part is on.
108 /// * `address` - the part's 7-bit address.
109 ///
110 /// # Returns
111 ///
112 /// The register map.
113 pub fn new(bus: I2C, address: u8) -> I2cRegisters<I2C> {
114 I2cRegisters { bus, address }
115 }
116
117 /// Returns the part's 7-bit address.
118 pub fn address(&self) -> u8 {
119 self.address
120 }
121
122 /// Gives back the bus.
123 ///
124 /// # Returns
125 ///
126 /// The bus the map was built from.
127 pub fn release(self) -> I2C {
128 self.bus
129 }
130}
131
132impl<I2C: I2c> RegisterBus for I2cRegisters<I2C> {
133 type Error = I2C::Error;
134
135 fn read_registers(&mut self, first: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
136 self.bus.write_read(self.address, &[first], buffer)
137 }
138
139 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Self::Error> {
140 self.bus.write(self.address, &[register, value])
141 }
142}
143
144/// A register map behind an SPI chip select, in the Bosch framing.
145///
146/// The first byte of every transfer is the control byte: the register address with
147/// bit 7 set to read or cleared to write. On a read the part then streams consecutive
148/// registers; on a write the value follows. The part accepts SPI modes 0 and 3.
149#[derive(Debug)]
150pub struct SpiRegisters<SPI> {
151 device: SPI,
152}
153
154impl<SPI> SpiRegisters<SPI> {
155 /// Wraps an SPI device, which is the bus plus the part's chip-select line.
156 ///
157 /// # Arguments
158 ///
159 /// * `device` - the SPI device.
160 ///
161 /// # Returns
162 ///
163 /// The register map.
164 pub fn new(device: SPI) -> SpiRegisters<SPI> {
165 SpiRegisters { device }
166 }
167
168 /// Gives back the device.
169 ///
170 /// # Returns
171 ///
172 /// The device the map was built from.
173 pub fn release(self) -> SPI {
174 self.device
175 }
176}
177
178impl<SPI: SpiDevice> RegisterBus for SpiRegisters<SPI> {
179 type Error = SPI::Error;
180
181 fn read_registers(&mut self, first: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
182 let control = [first | SPI_READ];
183 self.device
184 .transaction(&mut [Operation::Write(&control), Operation::Read(buffer)])
185 }
186
187 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Self::Error> {
188 self.device.write(&[register & SPI_ADDRESS_MASK, value])
189 }
190}
191
192/// Reads a 16-bit register that travels most significant byte first.
193///
194/// # Arguments
195///
196/// * `bus` - the I2C bus.
197/// * `address` - the part's 7-bit address.
198/// * `register` - the register address, or pointer value, to read.
199///
200/// # Returns
201///
202/// The register's value.
203///
204/// # Errors
205///
206/// Returns the bus error if the part does not answer.
207///
208/// # Examples
209///
210/// ```
211/// use pamoja_hal::script::{I2cScript, I2cStep};
212/// use pamoja_sensors::driver::read_word;
213/// use pamoja_sensors::tmp117::{address, register, DEVICE_ID};
214///
215/// // A TMP117 reads its device id register as one word, high byte first.
216/// const PART: u8 = address::ADD0_GND;
217/// let answer = DEVICE_ID.to_be_bytes();
218/// let mut bus = I2cScript::new([I2cStep::write_read(PART, [register::DEVICE_ID], answer)]);
219/// assert_eq!(read_word(&mut bus, PART, register::DEVICE_ID)?, DEVICE_ID);
220/// # Ok::<(), pamoja_hal::script::ScriptError>(())
221/// ```
222pub fn read_word<I2C: I2c>(bus: &mut I2C, address: u8, register: u8) -> Result<u16, I2C::Error> {
223 let mut bytes = [0u8; 2];
224 bus.write_read(address, &[register], &mut bytes)?;
225 Ok(u16::from_be_bytes(bytes))
226}
227
228/// Writes a 16-bit register that travels most significant byte first.
229///
230/// # Arguments
231///
232/// * `bus` - the I2C bus.
233/// * `address` - the part's 7-bit address.
234/// * `register` - the register address, or pointer value, to write.
235/// * `value` - the value to write.
236///
237/// # Errors
238///
239/// Returns the bus error if the part does not answer.
240pub fn write_word<I2C: I2c>(
241 bus: &mut I2C,
242 address: u8,
243 register: u8,
244 value: u16,
245) -> Result<(), I2C::Error> {
246 let [high, low] = value.to_be_bytes();
247 bus.write(address, &[register, high, low])
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use pamoja_hal::script::{I2cScript, I2cStep, SpiScript, SpiStep};
254
255 #[test]
256 fn i2c_registers_read_with_a_repeated_start_and_write_address_then_value() {
257 let script = I2cScript::new([
258 I2cStep::write_read(0x77, [0xF7], [1, 2, 3, 4, 5, 6, 7, 8]),
259 I2cStep::write(0x77, [0xF4, 0x25]),
260 ]);
261 let mut part = I2cRegisters::new(script, 0x77);
262 let mut data = [0u8; 8];
263 part.read_registers(0xF7, &mut data).unwrap();
264 assert_eq!(data, [1, 2, 3, 4, 5, 6, 7, 8]);
265 part.write_register(0xF4, 0x25).unwrap();
266 assert_eq!(part.address(), 0x77);
267 assert!(part.release().done());
268 }
269
270 #[test]
271 fn spi_registers_set_the_read_flag_and_clear_it_to_write() {
272 let script = SpiScript::new([
273 SpiStep::write([0xD0]),
274 SpiStep::read([0x60]),
275 SpiStep::write([0x74, 0x25]),
276 ]);
277 let mut part = SpiRegisters::new(script);
278 assert_eq!(part.read_register(0xD0).unwrap(), 0x60);
279 part.write_register(0xF4, 0x25).unwrap();
280 assert!(part.release().done());
281 }
282
283 #[test]
284 fn words_travel_most_significant_byte_first() {
285 let mut bus = I2cScript::new([
286 I2cStep::write(0x40, [0x05, 0x10, 0x00]),
287 I2cStep::write_read(0x40, [0x02], [0x1F, 0x40]),
288 ]);
289 write_word(&mut bus, 0x40, 0x05, 0x1000).unwrap();
290 assert_eq!(read_word(&mut bus, 0x40, 0x02).unwrap(), 0x1F40);
291 assert!(bus.done());
292 }
293}