1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include "pw_bytes/endian.h"
17 #include "pw_bytes/span.h"
18 #include "pw_chrono/system_clock.h"
19 #include "pw_i2c/address.h"
20 #include "pw_i2c/device.h"
21 #include "pw_i2c/initiator.h"
22 #include "pw_result/result.h"
23 #include "pw_status/status.h"
24 #include "pw_status/try.h"
25
26 namespace pw {
27 namespace i2c {
28
29 enum class RegisterAddressSize {
30 k1Byte = 1,
31 k2Bytes = 2,
32 k4Bytes = 4,
33 };
34
35 /// The common interface for I2C register devices. Contains methods to help
36 /// read and write the device's registers.
37 ///
38 /// @warning This interface assumes that you know how to consult your device's
39 /// datasheet to determine correct address sizes, data sizes, endianness, etc.
40 class RegisterDevice : public Device {
41 public:
42 /// This constructor specifies the endianness of the register address and
43 /// data separately. If your register address and data have the same
44 /// endianness and you'd like to specify them both with a single argument,
45 /// see the other `pw::i2c::RegisterDevice` constructor.
46 ///
47 /// @param[in] initiator A `pw::i2c::Initiator` instance for the bus that the
48 /// device is on.
49 ///
50 /// @param[in] address The address of the I2C device.
51 ///
52 /// @param[in] register_address_order The endianness of the register address.
53 ///
54 /// @param[in] data_order The endianness of the data.
55 ///
56 /// @param[in] register_address_size The size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian register_address_order,endian data_order,RegisterAddressSize register_address_size)57 constexpr RegisterDevice(Initiator& initiator,
58 Address address,
59 endian register_address_order,
60 endian data_order,
61 RegisterAddressSize register_address_size)
62 : Device(initiator, address),
63 register_address_order_(register_address_order),
64 data_order_(data_order),
65 register_address_size_(register_address_size) {}
66
67 /// This constructor specifies the endianness of the register address and
68 /// data with a single argument. If your register address and data have
69 /// different endianness, use the other `pw::i2c::RegisterDevice`
70 /// constructor.
71 ///
72 /// @param[in] initiator A `pw::i2c::Initiator` instance for the bus that the
73 /// device is on.
74 ///
75 /// @param[in] address The address of the I2C device.
76 ///
77 /// @param[in] order The endianness of both the register address and register
78 /// data.
79 ///
80 /// @param[in] register_address_size The size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian order,RegisterAddressSize register_address_size)81 constexpr RegisterDevice(Initiator& initiator,
82 Address address,
83 endian order,
84 RegisterAddressSize register_address_size)
85 : Device(initiator, address),
86 register_address_order_(order),
87 data_order_(order),
88 register_address_size_(register_address_size) {}
89
90 /// Writes data to multiple contiguous registers starting at a specific
91 /// register. This method is byte-addressable.
92 ///
93 /// `register_address` and `register_data` use the endianness that was
94 /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
95 ///
96 /// @pre This method assumes that you've verified that your device supports
97 /// bulk writes and that `register_data` is a correct size for your device.
98 ///
99 /// @param[in] register_address The register address to begin writing at.
100 ///
101 /// @param[in] register_data The data to write. Endianness is taken into
102 /// account if the data is 2 or 4 bytes.
103 ///
104 /// @param[in] buffer A buffer for constructing the write data. The size of
105 /// this buffer must be at least as large as the size of `register_address`
106 /// plus the size of `register_data`.
107 ///
108 /// @param[in] timeout The maximum duration to block waiting for both
109 /// exclusive bus access and the completion of the I2C transaction.
110 ///
111 /// @returns @rst
112 ///
113 /// .. pw-status-codes::
114 ///
115 /// OK: The bulk write was successful.
116 ///
117 /// DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
118 /// complete the transaction in time.
119 ///
120 /// FAILED_PRECONDITION: The interface is not initialized or enabled.
121 ///
122 /// INTERNAL: An issue occurred while building ``register_data``.
123 ///
124 /// INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
125 /// address space.
126 ///
127 /// OUT_OF_RANGE: The size of ``buffer`` is less than the size
128 /// of ``register_address`` plus the size of ``register_data``.
129 ///
130 /// UNAVAILABLE: The device took too long to respond to the NACK.
131 ///
132 /// @endrst
133 Status WriteRegisters(uint32_t register_address,
134 ConstByteSpan register_data,
135 ByteSpan buffer,
136 chrono::SystemClock::duration timeout);
137
138 /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
139 /// `register_data` to be exactly 8 bits.
140 Status WriteRegisters8(uint32_t register_address,
141 span<const uint8_t> register_data,
142 ByteSpan buffer,
143 chrono::SystemClock::duration timeout);
144
145 /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
146 /// `register_data` to be exactly 16 bits.
147 Status WriteRegisters16(uint32_t register_address,
148 span<const uint16_t> register_data,
149 ByteSpan buffer,
150 chrono::SystemClock::duration timeout);
151
152 /// Variant of `pw::i2c::RegisterDevice::WriteRegisters()` that requires
153 /// `register_data` to be exactly 32 bits.
154 Status WriteRegisters32(uint32_t register_address,
155 span<const uint32_t> register_data,
156 ByteSpan buffer,
157 chrono::SystemClock::duration timeout);
158
159 /// Reads data from multiple contiguous registers starting from a specific
160 /// offset or register. This method is byte-addressable.
161 ///
162 /// `register_address` and `return_data` use the endianness that was
163 /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
164 ///
165 /// @pre This method assumes that you've verified that your device supports
166 /// bulk reads and that `return_data` is a correct size for your device.
167 ///
168 /// @param[in] register_address The register address to begin reading at.
169 ///
170 /// @param[out] return_data The area to read the data into. The amount of
171 /// data that will be read is equal to the size of this span. Endianness is
172 /// taken into account if this span is 2 or 4 bytes.
173 ///
174 /// @param[in] timeout The maximum duration to block waiting for both
175 /// exclusive bus access and the completion of the I2C transaction.
176 ///
177 /// @returns @rst
178 ///
179 /// .. pw-status-codes::
180 ///
181 /// OK: The bulk read was successful.
182 ///
183 /// DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
184 /// complete the transaction in time.
185 ///
186 /// FAILED_PRECONDITION: The interface is not initialized or enabled.
187 ///
188 /// INTERNAL: An issue occurred while building ``return_data``.
189 ///
190 /// INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
191 /// address space.
192 ///
193 /// UNAVAILABLE: The device took too long to respond to the NACK.
194 ///
195 /// @endrst
196 Status ReadRegisters(uint32_t register_address,
197 ByteSpan return_data,
198 chrono::SystemClock::duration timeout);
199
200 /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
201 /// `return_data` to be exactly 8 bits.
202 Status ReadRegisters8(uint32_t register_address,
203 span<uint8_t> return_data,
204 chrono::SystemClock::duration timeout);
205
206 /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
207 /// `return_data` to be exactly 16 bits.
208 Status ReadRegisters16(uint32_t register_address,
209 span<uint16_t> return_data,
210 chrono::SystemClock::duration timeout);
211
212 /// Variant of `pw::i2c::RegisterDevice::ReadRegisters()` that requires
213 /// `return_data` to be exactly 32 bits.
214 Status ReadRegisters32(uint32_t register_address,
215 span<uint32_t> return_data,
216 chrono::SystemClock::duration timeout);
217
218 /// Sends a register address to write to and then writes to that address.
219 ///
220 /// `register_address` and `register_data` use the endianness that was
221 /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
222 ///
223 /// @pre This method assumes that you've verified that `register_data` is a
224 /// correct size for your device.
225 ///
226 /// @param[in] register_address The register address to write to.
227 ///
228 /// @param[in] register_data The data that should be written at the address.
229 /// The maximum allowed size is 4 bytes.
230 ///
231 /// @param[in] timeout The maximum duration to block waiting for both
232 /// exclusive bus access and the completion of the I2C transaction.
233 ///
234 /// @returns @rst
235 ///
236 /// .. pw-status-codes::
237 ///
238 /// OK: The write was successful.
239 ///
240 /// DEADLINE_EXCEEDED: Unable to acquire exclusive bus access
241 /// and complete the transaction in time.
242 ///
243 /// FAILED_PRECONDITION: The interface is not initialized or enabled.
244 ///
245 /// INTERNAL: An issue occurred while writing the data.
246 ///
247 /// INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
248 /// address space.
249 ///
250 /// UNAVAILABLE: The device took too long to respond to the NACK.
251 ///
252 /// @endrst
253 Status WriteRegister(uint32_t register_address,
254 std::byte register_data,
255 chrono::SystemClock::duration timeout);
256
257 /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
258 /// 8 bits.
259 Status WriteRegister8(uint32_t register_address,
260 uint8_t register_data,
261 chrono::SystemClock::duration timeout);
262
263 /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
264 /// 16 bits.
265 Status WriteRegister16(uint32_t register_address,
266 uint16_t register_data,
267 chrono::SystemClock::duration timeout);
268
269 /// Variant of `pw::i2c::RegisterDevice::WriteRegister()` that writes exactly
270 /// 32 bits.
271 Status WriteRegister32(uint32_t register_address,
272 uint32_t register_data,
273 chrono::SystemClock::duration timeout);
274
275 /// Sends a register address to read from and then reads from that address.
276 ///
277 /// `register_address` and the return data use the endianness that was
278 /// provided when this `pw::i2c::RegisterDevice` instance was constructed.
279 ///
280 /// @pre This method assumes that you've verified that the return data size
281 /// is a correct size for your device.
282 ///
283 /// @param[in] register_address The register address to read.
284 ///
285 /// @param[in] timeout The maximum duration to block waiting for both
286 /// exclusive bus access and the completion of the I2C transaction.
287 ///
288 /// @returns @rst
289 ///
290 /// .. pw-status-codes::
291 ///
292 /// OK: Returns the register data.
293 ///
294 /// DEADLINE_EXCEEDED: Unable to acquire exclusive bus access and
295 /// complete the transaction in time.
296 ///
297 /// FAILED_PRECONDITION: The interface is not initialized or enabled.
298 ///
299 /// INTERNAL: An issue occurred while building the return data.
300 ///
301 /// INVALID_ARGUMENT: ``register_address`` is larger than the 10-bit
302 /// address space.
303 ///
304 /// UNAVAILABLE: The device took too long to respond to the NACK.
305 ///
306 /// @endrst
307 Result<std::byte> ReadRegister(uint32_t register_address,
308 chrono::SystemClock::duration timeout);
309
310 /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
311 /// 8 bits.
312 Result<uint8_t> ReadRegister8(uint32_t register_address,
313 chrono::SystemClock::duration timeout);
314
315 /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
316 /// 16 bits.
317 Result<uint16_t> ReadRegister16(uint32_t register_address,
318 chrono::SystemClock::duration timeout);
319
320 /// Variant of `pw::i2c::RegisterDevice::ReadRegister()` that returns exactly
321 /// 32 bits.
322 Result<uint32_t> ReadRegister32(uint32_t register_address,
323 chrono::SystemClock::duration timeout);
324
325 private:
326 // Helper write registers.
327 Status WriteRegisters(uint32_t register_address,
328 ConstByteSpan register_data,
329 const size_t register_data_size,
330 ByteSpan buffer,
331 chrono::SystemClock::duration timeout);
332
333 const endian register_address_order_;
334 const endian data_order_;
335 const RegisterAddressSize register_address_size_;
336 };
337
WriteRegisters(uint32_t register_address,ConstByteSpan register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)338 inline Status RegisterDevice::WriteRegisters(
339 uint32_t register_address,
340 ConstByteSpan register_data,
341 ByteSpan buffer,
342 chrono::SystemClock::duration timeout) {
343 return WriteRegisters(register_address,
344 register_data,
345 sizeof(decltype(register_data)::value_type),
346 buffer,
347 timeout);
348 }
349
WriteRegisters8(uint32_t register_address,span<const uint8_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)350 inline Status RegisterDevice::WriteRegisters8(
351 uint32_t register_address,
352 span<const uint8_t> register_data,
353 ByteSpan buffer,
354 chrono::SystemClock::duration timeout) {
355 return WriteRegisters(register_address,
356 as_bytes(register_data),
357 sizeof(decltype(register_data)::value_type),
358 buffer,
359 timeout);
360 }
361
WriteRegisters16(uint32_t register_address,span<const uint16_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)362 inline Status RegisterDevice::WriteRegisters16(
363 uint32_t register_address,
364 span<const uint16_t> register_data,
365 ByteSpan buffer,
366 chrono::SystemClock::duration timeout) {
367 return WriteRegisters(register_address,
368 as_bytes(register_data),
369 sizeof(decltype(register_data)::value_type),
370 buffer,
371 timeout);
372 }
373
WriteRegisters32(uint32_t register_address,span<const uint32_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)374 inline Status RegisterDevice::WriteRegisters32(
375 uint32_t register_address,
376 span<const uint32_t> register_data,
377 ByteSpan buffer,
378 chrono::SystemClock::duration timeout) {
379 return WriteRegisters(register_address,
380 as_bytes(register_data),
381 sizeof(decltype(register_data)::value_type),
382 buffer,
383 timeout);
384 }
385
WriteRegister(uint32_t register_address,std::byte register_data,chrono::SystemClock::duration timeout)386 inline Status RegisterDevice::WriteRegister(
387 uint32_t register_address,
388 std::byte register_data,
389 chrono::SystemClock::duration timeout) {
390 std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
391 byte_buffer;
392 return WriteRegisters(register_address,
393 span(®ister_data, 1),
394 sizeof(register_data),
395 byte_buffer,
396 timeout);
397 }
398
WriteRegister8(uint32_t register_address,uint8_t register_data,chrono::SystemClock::duration timeout)399 inline Status RegisterDevice::WriteRegister8(
400 uint32_t register_address,
401 uint8_t register_data,
402 chrono::SystemClock::duration timeout) {
403 std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
404 byte_buffer;
405 return WriteRegisters(register_address,
406 as_bytes(span(®ister_data, 1)),
407 sizeof(register_data),
408 byte_buffer,
409 timeout);
410 }
411
WriteRegister16(uint32_t register_address,uint16_t register_data,chrono::SystemClock::duration timeout)412 inline Status RegisterDevice::WriteRegister16(
413 uint32_t register_address,
414 uint16_t register_data,
415 chrono::SystemClock::duration timeout) {
416 std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
417 byte_buffer;
418 return WriteRegisters(register_address,
419 as_bytes(span(®ister_data, 1)),
420 sizeof(register_data),
421 byte_buffer,
422 timeout);
423 }
424
WriteRegister32(uint32_t register_address,uint32_t register_data,chrono::SystemClock::duration timeout)425 inline Status RegisterDevice::WriteRegister32(
426 uint32_t register_address,
427 uint32_t register_data,
428 chrono::SystemClock::duration timeout) {
429 std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
430 byte_buffer;
431 return WriteRegisters(register_address,
432 as_bytes(span(®ister_data, 1)),
433 sizeof(register_data),
434 byte_buffer,
435 timeout);
436 }
437
ReadRegisters8(uint32_t register_address,span<uint8_t> return_data,chrono::SystemClock::duration timeout)438 inline Status RegisterDevice::ReadRegisters8(
439 uint32_t register_address,
440 span<uint8_t> return_data,
441 chrono::SystemClock::duration timeout) {
442 // For a single byte, there's no endian data, and so we can return the
443 // data as is.
444 return ReadRegisters(
445 register_address, as_writable_bytes(return_data), timeout);
446 }
447
ReadRegisters16(uint32_t register_address,span<uint16_t> return_data,chrono::SystemClock::duration timeout)448 inline Status RegisterDevice::ReadRegisters16(
449 uint32_t register_address,
450 span<uint16_t> return_data,
451 chrono::SystemClock::duration timeout) {
452 PW_TRY(
453 ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
454
455 // Post process endian information.
456 for (uint16_t& register_value : return_data) {
457 register_value = bytes::ReadInOrder<uint16_t>(data_order_, ®ister_value);
458 }
459
460 return pw::OkStatus();
461 }
462
ReadRegisters32(uint32_t register_address,span<uint32_t> return_data,chrono::SystemClock::duration timeout)463 inline Status RegisterDevice::ReadRegisters32(
464 uint32_t register_address,
465 span<uint32_t> return_data,
466 chrono::SystemClock::duration timeout) {
467 PW_TRY(
468 ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
469
470 // TODO: b/185952662 - Extend endian in pw_byte to support this conversion
471 // as optimization.
472 // Post process endian information.
473 for (uint32_t& register_value : return_data) {
474 register_value = bytes::ReadInOrder<uint32_t>(data_order_, ®ister_value);
475 }
476
477 return pw::OkStatus();
478 }
479
ReadRegister(uint32_t register_address,chrono::SystemClock::duration timeout)480 inline Result<std::byte> RegisterDevice::ReadRegister(
481 uint32_t register_address, chrono::SystemClock::duration timeout) {
482 std::byte data = {};
483 PW_TRY(ReadRegisters(register_address, span(&data, 1), timeout));
484 return data;
485 }
486
ReadRegister8(uint32_t register_address,chrono::SystemClock::duration timeout)487 inline Result<uint8_t> RegisterDevice::ReadRegister8(
488 uint32_t register_address, chrono::SystemClock::duration timeout) {
489 uint8_t data = 0;
490 PW_TRY(ReadRegisters8(register_address, span(&data, 1), timeout));
491 return data;
492 }
493
ReadRegister16(uint32_t register_address,chrono::SystemClock::duration timeout)494 inline Result<uint16_t> RegisterDevice::ReadRegister16(
495 uint32_t register_address, chrono::SystemClock::duration timeout) {
496 std::array<uint16_t, 1> data = {};
497 PW_TRY(ReadRegisters16(register_address, data, timeout));
498 return data[0];
499 }
500
ReadRegister32(uint32_t register_address,chrono::SystemClock::duration timeout)501 inline Result<uint32_t> RegisterDevice::ReadRegister32(
502 uint32_t register_address, chrono::SystemClock::duration timeout) {
503 std::array<uint32_t, 1> data = {};
504 PW_TRY(ReadRegisters32(register_address, data, timeout));
505 return data[0];
506 }
507
508 } // namespace i2c
509 } // namespace pw
510
511 // TODO (zengk): Register modification.
512