• 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 //
15 #pragma once
16 
17 #include "pw_bytes/span.h"
18 #include "pw_chrono/system_clock.h"
19 #include "pw_i2c/address.h"
20 #include "pw_i2c/initiator.h"
21 #include "pw_status/status.h"
22 
23 namespace pw {
24 namespace i2c {
25 
26 // Device is used to write/read arbitrary chunks of data over a bus to a device.
27 // This object essentially just wrap the Initiator API with a fixed I2C device
28 // address.
29 class Device {
30  public:
Device(Initiator & initiator,Address device_address)31   constexpr Device(Initiator& initiator, Address device_address)
32       : initiator_(initiator), device_address_(device_address) {}
33 
34   Device(const Device&) = delete;
35   ~Device() = default;
36 
37   // Write bytes and then read bytes as either one atomic or two independent I2C
38   // transaction.
39   // The signal on the bus should appear as follows:
40   // 1) Write Only:
41   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
42   // 2) Read Only:
43   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
44   // 3A) Write + Read (atomic):
45   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES +
46   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
47   // 3B) Write + Read (separate):
48   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
49   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
50   //
51   // The timeout defines the minimum duration one may block waiting for both
52   // exclusive bus access and the completion of the I2C transaction.
53   //
54   // Preconditions:
55   // The Address must be supported by the Initiator, i.e. do not use a 10
56   //     address if the Initiator only supports 7 bit. This will assert.
57   //
58   // Returns:
59   // Ok - Success.
60   // InvalidArgument - device_address is larger than the 10 bit address space.
61   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
62   //   and complete the I2C transaction in time.
63   // Unavailable - NACK condition occurred, meaning the addressed device did
64   //   not respond or was unable to process the request.
65   // FailedPrecondition - The interface is not currently initialized and/or
66   //    enabled.
WriteReadFor(ConstByteSpan tx_buffer,ByteSpan rx_buffer,chrono::SystemClock::duration timeout)67   Status WriteReadFor(ConstByteSpan tx_buffer,
68                       ByteSpan rx_buffer,
69                       chrono::SystemClock::duration timeout) {
70     return initiator_.WriteReadFor(
71         device_address_, tx_buffer, rx_buffer, timeout);
72   }
WriteReadFor(const void * tx_buffer,size_t tx_size_bytes,void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration timeout)73   Status WriteReadFor(const void* tx_buffer,
74                       size_t tx_size_bytes,
75                       void* rx_buffer,
76                       size_t rx_size_bytes,
77                       chrono::SystemClock::duration timeout) {
78     return initiator_.WriteReadFor(device_address_,
79                                    tx_buffer,
80                                    tx_size_bytes,
81                                    rx_buffer,
82                                    rx_size_bytes,
83                                    timeout);
84   }
85 
86   // Write bytes. The signal on the bus should appear as follows:
87   //   START + I2C Address + WRITE(0) + TX_BUFFER_BYTES + STOP
88   //
89   // The timeout defines the minimum duration one may block waiting for both
90   // exclusive bus access and the completion of the I2C transaction.
91   //
92   // Preconditions:
93   // The Address must be supported by the Initiator, i.e. do not use a 10
94   //     address if the Initiator only supports 7 bit. This will assert.
95   //
96   // Returns:
97   // Ok - Success.
98   // InvalidArgument - device_address is larger than the 10 bit address space.
99   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
100   //   and complete the I2C transaction in time.
101   // Unavailable - NACK condition occurred, meaning the addressed device did
102   //   not respond or was unable to process the request.
103   // FailedPrecondition - The interface is not currently initialized and/or
104   //    enabled.
WriteFor(ConstByteSpan tx_buffer,chrono::SystemClock::duration timeout)105   Status WriteFor(ConstByteSpan tx_buffer,
106                   chrono::SystemClock::duration timeout) {
107     return initiator_.WriteFor(device_address_, tx_buffer, timeout);
108   }
WriteFor(const void * tx_buffer,size_t tx_size_bytes,chrono::SystemClock::duration timeout)109   Status WriteFor(const void* tx_buffer,
110                   size_t tx_size_bytes,
111                   chrono::SystemClock::duration timeout) {
112     return initiator_.WriteFor(
113         device_address_, tx_buffer, tx_size_bytes, timeout);
114   }
115 
116   // Read bytes. The signal on the bus should appear as follows:
117   //   START + I2C Address + READ(1) + RX_BUFFER_BYTES + STOP
118   //
119   // The timeout defines the minimum duration one may block waiting for both
120   // exclusive bus access and the completion of the I2C transaction.
121   //
122   // Preconditions:
123   // The Address must be supported by the Initiator, i.e. do not use a 10
124   //     address if the Initiator only supports 7 bit. This will assert.
125   //
126   // Returns:
127   // Ok - Success.
128   // InvalidArgument - device_address is larger than the 10 bit address space.
129   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
130   //   and complete the I2C transaction in time.
131   // Unavailable - NACK condition occurred, meaning the addressed device did
132   //   not respond or was unable to process the request.
133   // FailedPrecondition - The interface is not currently initialized and/or
134   //    enabled.
ReadFor(ByteSpan rx_buffer,chrono::SystemClock::duration timeout)135   Status ReadFor(ByteSpan rx_buffer, chrono::SystemClock::duration timeout) {
136     return initiator_.ReadFor(device_address_, rx_buffer, timeout);
137   }
ReadFor(void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration timeout)138   Status ReadFor(void* rx_buffer,
139                  size_t rx_size_bytes,
140                  chrono::SystemClock::duration timeout) {
141     return initiator_.ReadFor(
142         device_address_, rx_buffer, rx_size_bytes, timeout);
143   }
144 
145   // Probes the device for an I2C ACK after only writing the address.
146   // This is done by attempting to read a single byte from the specified device.
147   //
148   // The timeout defines the minimum duration one may block waiting for both
149   // exclusive bus access and the completion of the I2C transaction.
150   //
151   // Preconditions:
152   // The Address must be supported by the Initiator, i.e. do not use a 10
153   //     address if the Initiator only supports 7 bit. This will assert.
154   //
155   // Returns:
156   // Ok - Success.
157   // InvalidArgument - device_address is larger than the 10 bit address space.
158   // DeadlineExceeded - Was unable to acquire exclusive Initiator access
159   //   and complete the I2C transaction in time.
160   // Unavailable - NACK condition occurred, meaning the addressed device did
161   //   not respond or was unable to process the request.
162   // FailedPrecondition - The interface is not currently initialized and/or
163   //    enabled.
ProbeFor(chrono::SystemClock::duration timeout)164   Status ProbeFor(chrono::SystemClock::duration timeout) {
165     return initiator_.ProbeDeviceFor(device_address_, timeout);
166   }
167 
168  private:
169   Initiator& initiator_;
170   const Address device_address_;
171 };
172 
173 }  // namespace i2c
174 }  // namespace pw
175