• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // RegisterDevice is used to write/read registers, chunks of data
36 // or just an array of bytes over a bus to a device.
37 //
38 // DISCLAIMER:
39 // It is important to note that bulk write/read may not be supported for every
40 // device and that it's up to the user to know the capabilities of their device.
41 // Users should also be aware of the register and address size and use the
42 // appropriate methods for their device.
43 //
44 //  - WriteRegisters*
45 //       Write to a set of registers starting at a specific address/offset.
46 //       Endianness will be applied to data that's read or written.
47 //
48 //  - WriteRegister*
49 //       Write data to a register where the max register size is 4 bytes.
50 //       Endianness will be applied to data that's read or written.
51 //
52 //  - ReadRegisters*
53 //       Read a set of registers starting at a specific address/offset.
54 //       Endianness will be applied to data that's read or written.
55 //
56 //  - ReadRegister*
57 //       Read data to a register where the max register size is 4 bytes.
58 //       Endianness will be applied to data that's read or written.
59 class RegisterDevice : public Device {
60  public:
61   // Args:
62   //   initiator: I2C initiator for the bus the device is on.
63   //   address: I2C device address.
64   //   register_address_order: Endianness of the register address.
65   //   data_order: Endianness of the data.
66   //   register_address_size: Size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian register_address_order,endian data_order,RegisterAddressSize register_address_size)67   constexpr RegisterDevice(Initiator& initiator,
68                            Address address,
69                            endian register_address_order,
70                            endian data_order,
71                            RegisterAddressSize register_address_size)
72       : Device(initiator, address),
73         register_address_order_(register_address_order),
74         data_order_(data_order),
75         register_address_size_(register_address_size) {}
76 
77   // Args:
78   //   initiator: I2C initiator for the bus the device is on.
79   //   address: I2C device address.
80   //   order: Endianness of the register address and data.
81   //   register_address_size: Size of the register address.
RegisterDevice(Initiator & initiator,Address address,endian order,RegisterAddressSize register_address_size)82   constexpr RegisterDevice(Initiator& initiator,
83                            Address address,
84                            endian order,
85                            RegisterAddressSize register_address_size)
86       : Device(initiator, address),
87         register_address_order_(order),
88         data_order_(order),
89         register_address_size_(register_address_size) {}
90 
91   // Writes data to multiple contiguous registers starting at specific register.
92   // WriteRegisters has byte addressable capabilities and it is up to the user
93   // to determine the appropriate size based on the features of the device. The
94   // amount of data to write is the size of the span. Endianness is taken into
95   // account if register_data_size is 2 bytes or 4 bytes. Both address and
96   // data will use the same endianness provided by the constructor.
97   //
98   // It is important to note that bulk write may not be supported for every
99   // device and that it's up to the user to know the capabilities of their
100   // device. Args:
101   //   register_address: Register address to send.
102   //   register_data: Data to write.
103   //   buffer: Since we need a buffer to construct the write data that consists
104   //           of the register address and the register data, the buffer should
105   //           be big enough such that the two can be concatenated.
106   //   timeout: timeout that's used for both lock and transaction.
107   // Returns:
108   //   Ok: Successful.
109   //   DeadlineExceeded: Unable to acquire exclusive Initiator access and
110   //                     complete the I2C transaction in time.
111   //   FailedPrecondition: Interface is not initialized and/or enabled.
112   //   Internal: Building data for the write buffer has an issue.
113   //   InvalidArgument: Device_address is larger than the 10 bit address space.
114   //   OutOfRange: if buffer size is too small for data and register_address.
115   //   Unavailable: if NACK and device did not respond in time.
116   Status WriteRegisters(uint32_t register_address,
117                         ConstByteSpan register_data,
118                         ByteSpan buffer,
119                         chrono::SystemClock::duration timeout);
120 
121   Status WriteRegisters8(uint32_t register_address,
122                          span<const uint8_t> register_data,
123                          ByteSpan buffer,
124                          chrono::SystemClock::duration timeout);
125 
126   Status WriteRegisters16(uint32_t register_address,
127                           span<const uint16_t> register_data,
128                           ByteSpan buffer,
129                           chrono::SystemClock::duration timeout);
130 
131   Status WriteRegisters32(uint32_t register_address,
132                           span<const uint32_t> register_data,
133                           ByteSpan buffer,
134                           chrono::SystemClock::duration timeout);
135 
136   // Reads data chunk starting at specific offset or register.
137   // ReadRegisters has byte addressable capabilities and it is up to the user
138   // to determine the appropriate size based on the features of the device. The
139   // amount of data to read is the size of the span. Endianness is taken into
140   // account for the *16 and *32 bit methods.  Both address and data will use
141   // the same endianness provided by the constructor.
142   //
143   // It is important to note that bulk read may not be supported for every
144   // device and that it's up to the user to know the capabilities of their
145   // device. Args:
146   //   register_address: Register address to send.
147   //   return_data: Area to read data to.
148   //   timeout: Timeout that's used for both lock and transaction.
149   // Returns:
150   //   Ok: Successful.
151   //   DeadlineExceeded: Unable to acquire exclusive Initiator access and
152   //                     complete the I2C transaction in time.
153   //   FailedPrecondition: Interface is not initialized and/or enabled.
154   //   Internal: Building data for the write buffer has an issue.
155   //   InvalidArgument: Device_address is larger than the 10 bit address space.
156   //   Unavailable: if NACK and device did not respond in time.
157   Status ReadRegisters(uint32_t register_address,
158                        ByteSpan return_data,
159                        chrono::SystemClock::duration timeout);
160 
161   Status ReadRegisters8(uint32_t register_address,
162                         span<uint8_t> return_data,
163                         chrono::SystemClock::duration timeout);
164 
165   Status ReadRegisters16(uint32_t register_address,
166                          span<uint16_t> return_data,
167                          chrono::SystemClock::duration timeout);
168 
169   Status ReadRegisters32(uint32_t register_address,
170                          span<uint32_t> return_data,
171                          chrono::SystemClock::duration timeout);
172 
173   // Writes the register address first before data.
174   // User should be careful which WriteRegister* API is used and should use
175   // the one that matches their register data size if not byte addressable.
176   //
177   // Both address and data will use the same endianness provided by the
178   // constructor.
179   // Args:
180   //   register_address: Register address to send.
181   //   register_data: Data to write.
182   //   timeout: Timeout that's used for both lock and transaction.
183   // Returns:
184   //   Ok: Successful.
185   //   DeadlineExceeded: Unable to acquire exclusive Initiator access and
186   //                     complete the I2C transaction in time.
187   //   FailedPrecondition: Interface is not initialized and/or enabled.
188   //   Internal: Building data for the write buffer has an issue.
189   //   InvalidArgument: Device_address is larger than the 10 bit address space.
190   //   Unavailable: if NACK and device did not respond in time.
191   Status WriteRegister(uint32_t register_address,
192                        std::byte register_data,
193                        chrono::SystemClock::duration timeout);
194 
195   Status WriteRegister8(uint32_t register_address,
196                         uint8_t register_data,
197                         chrono::SystemClock::duration timeout);
198 
199   Status WriteRegister16(uint32_t register_address,
200                          uint16_t register_data,
201                          chrono::SystemClock::duration timeout);
202 
203   Status WriteRegister32(uint32_t register_address,
204                          uint32_t register_data,
205                          chrono::SystemClock::duration timeout);
206 
207   // Reads data from the device after sending the register address first.
208   // User should be careful which ReadRegister* API is used and should use
209   // the one that matches their register data size if not byte addressable.
210   //
211   // Both address and data will use the same endianness provided by the
212   // constructor.
213   // Args:
214   //   register_address: Register address to send.
215   //   timeout: Timeout that's used for both lock and transaction.
216   // Returns:
217   //   Ok: Successful.
218   //   DeadlineExceeded: Unable to acquire exclusive Initiator access and
219   //                     complete the I2C transaction in time.
220   //   FailedPrecondition: Interface is not initialized and/or enabled.
221   //   Internal: Building data for the write buffer has an issue.
222   //   InvalidArgument: Device_address is larger than the 10 bit address space.
223   //   Unavailable: if NACK and device did not respond in time.
224   Result<std::byte> ReadRegister(uint32_t register_address,
225                                  chrono::SystemClock::duration timeout);
226 
227   Result<uint8_t> ReadRegister8(uint32_t register_address,
228                                 chrono::SystemClock::duration timeout);
229 
230   Result<uint16_t> ReadRegister16(uint32_t register_address,
231                                   chrono::SystemClock::duration timeout);
232 
233   Result<uint32_t> ReadRegister32(uint32_t register_address,
234                                   chrono::SystemClock::duration timeout);
235 
236  private:
237   // Helper write registers.
238   Status WriteRegisters(uint32_t register_address,
239                         ConstByteSpan register_data,
240                         const size_t register_data_size,
241                         ByteSpan buffer,
242                         chrono::SystemClock::duration timeout);
243 
244   const endian register_address_order_;
245   const endian data_order_;
246   const RegisterAddressSize register_address_size_;
247 };
248 
WriteRegisters(uint32_t register_address,ConstByteSpan register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)249 inline Status RegisterDevice::WriteRegisters(
250     uint32_t register_address,
251     ConstByteSpan register_data,
252     ByteSpan buffer,
253     chrono::SystemClock::duration timeout) {
254   return WriteRegisters(register_address,
255                         register_data,
256                         sizeof(decltype(register_data)::value_type),
257                         buffer,
258                         timeout);
259 }
260 
WriteRegisters8(uint32_t register_address,span<const uint8_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)261 inline Status RegisterDevice::WriteRegisters8(
262     uint32_t register_address,
263     span<const uint8_t> register_data,
264     ByteSpan buffer,
265     chrono::SystemClock::duration timeout) {
266   return WriteRegisters(register_address,
267                         as_bytes(register_data),
268                         sizeof(decltype(register_data)::value_type),
269                         buffer,
270                         timeout);
271 }
272 
WriteRegisters16(uint32_t register_address,span<const uint16_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)273 inline Status RegisterDevice::WriteRegisters16(
274     uint32_t register_address,
275     span<const uint16_t> register_data,
276     ByteSpan buffer,
277     chrono::SystemClock::duration timeout) {
278   return WriteRegisters(register_address,
279                         as_bytes(register_data),
280                         sizeof(decltype(register_data)::value_type),
281                         buffer,
282                         timeout);
283 }
284 
WriteRegisters32(uint32_t register_address,span<const uint32_t> register_data,ByteSpan buffer,chrono::SystemClock::duration timeout)285 inline Status RegisterDevice::WriteRegisters32(
286     uint32_t register_address,
287     span<const uint32_t> register_data,
288     ByteSpan buffer,
289     chrono::SystemClock::duration timeout) {
290   return WriteRegisters(register_address,
291                         as_bytes(register_data),
292                         sizeof(decltype(register_data)::value_type),
293                         buffer,
294                         timeout);
295 }
296 
WriteRegister(uint32_t register_address,std::byte register_data,chrono::SystemClock::duration timeout)297 inline Status RegisterDevice::WriteRegister(
298     uint32_t register_address,
299     std::byte register_data,
300     chrono::SystemClock::duration timeout) {
301   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
302       byte_buffer;
303   return WriteRegisters(register_address,
304                         span(&register_data, 1),
305                         sizeof(register_data),
306                         byte_buffer,
307                         timeout);
308 }
309 
WriteRegister8(uint32_t register_address,uint8_t register_data,chrono::SystemClock::duration timeout)310 inline Status RegisterDevice::WriteRegister8(
311     uint32_t register_address,
312     uint8_t register_data,
313     chrono::SystemClock::duration timeout) {
314   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
315       byte_buffer;
316   return WriteRegisters(register_address,
317                         as_bytes(span(&register_data, 1)),
318                         sizeof(register_data),
319                         byte_buffer,
320                         timeout);
321 }
322 
WriteRegister16(uint32_t register_address,uint16_t register_data,chrono::SystemClock::duration timeout)323 inline Status RegisterDevice::WriteRegister16(
324     uint32_t register_address,
325     uint16_t register_data,
326     chrono::SystemClock::duration timeout) {
327   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
328       byte_buffer;
329   return WriteRegisters(register_address,
330                         as_bytes(span(&register_data, 1)),
331                         sizeof(register_data),
332                         byte_buffer,
333                         timeout);
334 }
335 
WriteRegister32(uint32_t register_address,uint32_t register_data,chrono::SystemClock::duration timeout)336 inline Status RegisterDevice::WriteRegister32(
337     uint32_t register_address,
338     uint32_t register_data,
339     chrono::SystemClock::duration timeout) {
340   std::array<std::byte, sizeof(register_data) + sizeof(register_address)>
341       byte_buffer;
342   return WriteRegisters(register_address,
343                         as_bytes(span(&register_data, 1)),
344                         sizeof(register_data),
345                         byte_buffer,
346                         timeout);
347 }
348 
ReadRegisters8(uint32_t register_address,span<uint8_t> return_data,chrono::SystemClock::duration timeout)349 inline Status RegisterDevice::ReadRegisters8(
350     uint32_t register_address,
351     span<uint8_t> return_data,
352     chrono::SystemClock::duration timeout) {
353   // For a single byte, there's no endian data, and so we can return the
354   // data as is.
355   return ReadRegisters(
356       register_address, as_writable_bytes(return_data), timeout);
357 }
358 
ReadRegisters16(uint32_t register_address,span<uint16_t> return_data,chrono::SystemClock::duration timeout)359 inline Status RegisterDevice::ReadRegisters16(
360     uint32_t register_address,
361     span<uint16_t> return_data,
362     chrono::SystemClock::duration timeout) {
363   PW_TRY(
364       ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
365 
366   // Post process endian information.
367   for (uint16_t& register_value : return_data) {
368     register_value = bytes::ReadInOrder<uint16_t>(data_order_, &register_value);
369   }
370 
371   return pw::OkStatus();
372 }
373 
ReadRegisters32(uint32_t register_address,span<uint32_t> return_data,chrono::SystemClock::duration timeout)374 inline Status RegisterDevice::ReadRegisters32(
375     uint32_t register_address,
376     span<uint32_t> return_data,
377     chrono::SystemClock::duration timeout) {
378   PW_TRY(
379       ReadRegisters(register_address, as_writable_bytes(return_data), timeout));
380 
381   // TODO(b/185952662): Extend endian in pw_byte to support this conversion
382   //                    as optimization.
383   // Post process endian information.
384   for (uint32_t& register_value : return_data) {
385     register_value = bytes::ReadInOrder<uint32_t>(data_order_, &register_value);
386   }
387 
388   return pw::OkStatus();
389 }
390 
ReadRegister(uint32_t register_address,chrono::SystemClock::duration timeout)391 inline Result<std::byte> RegisterDevice::ReadRegister(
392     uint32_t register_address, chrono::SystemClock::duration timeout) {
393   std::byte data = {};
394   PW_TRY(ReadRegisters(register_address, span(&data, 1), timeout));
395   return data;
396 }
397 
ReadRegister8(uint32_t register_address,chrono::SystemClock::duration timeout)398 inline Result<uint8_t> RegisterDevice::ReadRegister8(
399     uint32_t register_address, chrono::SystemClock::duration timeout) {
400   uint8_t data = 0;
401   PW_TRY(ReadRegisters8(register_address, span(&data, 1), timeout));
402   return data;
403 }
404 
ReadRegister16(uint32_t register_address,chrono::SystemClock::duration timeout)405 inline Result<uint16_t> RegisterDevice::ReadRegister16(
406     uint32_t register_address, chrono::SystemClock::duration timeout) {
407   std::array<uint16_t, 1> data = {};
408   PW_TRY(ReadRegisters16(register_address, data, timeout));
409   return data[0];
410 }
411 
ReadRegister32(uint32_t register_address,chrono::SystemClock::duration timeout)412 inline Result<uint32_t> RegisterDevice::ReadRegister32(
413     uint32_t register_address, chrono::SystemClock::duration timeout) {
414   std::array<uint32_t, 1> data = {};
415   PW_TRY(ReadRegisters32(register_address, data, timeout));
416   return data[0];
417 }
418 
419 }  // namespace i2c
420 }  // namespace pw
421 
422 // TODO (zengk): Register modification.
423