1 // Copyright 2020 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 <cstdint> 17 18 #include "pw_bytes/span.h" 19 #include "pw_chrono/system_clock.h" 20 #include "pw_i2c/address.h" 21 #include "pw_status/status.h" 22 23 namespace pw::i2c { 24 25 /// @brief The common, base driver interface for initiating thread-safe 26 /// transactions with devices on an I2C bus. Other documentation may call this 27 /// style of interface an I2C "master", <!-- inclusive-language: disable --> 28 /// "central", or "controller". 29 /// 30 /// `pw::i2c::Initiator` isn't required to support 10-bit addressing. If only 31 /// 7-bit addressing is supported, `pw::i2c::Initiator` fails a runtime 32 /// assertion when given an address that is out of 7-bit address range. 33 /// 34 /// The implementer of this pure virtual interface is responsible for ensuring 35 /// thread safety and enabling functionality such as initialization, 36 /// configuration, enabling and disabling, unsticking SDA, and detecting device 37 /// address registration collisions. 38 /// 39 /// @note `pw::i2c::Initiator` uses internal synchronization, so it's safe to 40 /// initiate transactions from multiple threads. However, a combined write and 41 /// read transaction may not be atomic when there are multiple initiators on 42 /// the bus. Furthermore, devices may require specific sequences of 43 /// transactions, and application logic must provide the synchronization to 44 /// execute these sequences correctly. 45 class Initiator { 46 public: 47 virtual ~Initiator() = default; 48 49 /// Writes bytes to an I2C device and then reads bytes from that same 50 /// device as either one atomic I2C transaction or two independent I2C 51 /// transactions. 52 /// 53 /// If the I2C bus is a multi-initiator then the implementer of this 54 /// class **must** ensure it's a single-atomic transaction. 55 /// 56 /// The signal on the bus for the atomic transaction should look like this: 57 /// 58 /// @code 59 /// START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES + 60 /// START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP 61 /// @endcode 62 /// 63 /// The signal on the bus for the two independent transactions should look 64 /// like this: 65 /// 66 /// @code 67 /// START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES + STOP 68 /// START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP 69 /// @endcode 70 /// 71 /// @param[in] device_address The address of the I2C device. 72 /// 73 /// @param[in] tx_buffer The transmit buffer. 74 /// 75 /// @param[out] rx_buffer The receive buffer. 76 /// 77 /// @param[in] timeout The maximum duration to block waiting for both 78 /// exclusive bus access and the completion of the I2C transaction or 79 /// transactions. 80 /// 81 /// @pre The provided address must be supported by the initiator. I.e. 82 /// don't use a 10-bit address if the initiator only supports 7-bit 83 /// addresses. This method fails a runtime assertion if this precondition 84 /// isn't met. 85 /// 86 /// @returns @rst 87 /// 88 /// .. pw-status-codes:: 89 /// 90 /// OK: The transaction or transactions succeeded. 91 /// 92 /// INVALID_ARGUMENT: The device address provided is bigger than 10 bits. 93 /// 94 /// DEADLINE_EXCEEDED: Was unable to acquire exclusive initiator access 95 /// and complete the I2C transaction in time. 96 /// 97 /// UNAVAILABLE: A NACK condition occurred, meaning the addressed device 98 /// didn't respond or was unable to process the request. 99 /// 100 /// FAILED_PRECONDITION: The interface isn't initialized or enabled. 101 /// 102 /// @endrst WriteReadFor(Address device_address,ConstByteSpan tx_buffer,ByteSpan rx_buffer,chrono::SystemClock::duration timeout)103 Status WriteReadFor(Address device_address, 104 ConstByteSpan tx_buffer, 105 ByteSpan rx_buffer, 106 chrono::SystemClock::duration timeout) { 107 return DoWriteReadFor(device_address, tx_buffer, rx_buffer, timeout); 108 } 109 110 /// A variation of `pw::i2c::Initiator::WriteReadFor` that accepts explicit 111 /// sizes for the amount of bytes to write to the device and read from the 112 /// device. WriteReadFor(Address device_address,const void * tx_buffer,size_t tx_size_bytes,void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration timeout)113 Status WriteReadFor(Address device_address, 114 const void* tx_buffer, 115 size_t tx_size_bytes, 116 void* rx_buffer, 117 size_t rx_size_bytes, 118 chrono::SystemClock::duration timeout) { 119 return WriteReadFor( 120 device_address, 121 span(static_cast<const std::byte*>(tx_buffer), tx_size_bytes), 122 span(static_cast<std::byte*>(rx_buffer), rx_size_bytes), 123 timeout); 124 } 125 126 /// Write bytes to the I2C device. 127 /// 128 /// The signal on the bus should look like this: 129 /// 130 /// @code 131 /// START + I2C_ADDRESS + WRITE(0) + TX_BUFFER_BYTES + STOP 132 /// @endcode 133 /// 134 /// @param[in] device_address The address of the I2C device. 135 /// 136 /// @param[in] tx_buffer The transmit buffer. 137 /// 138 /// @param[out] rx_buffer The receive buffer. 139 /// 140 /// @param[in] timeout The maximum duration to block waiting for both 141 /// exclusive bus access and the completion of the I2C transaction. 142 /// 143 /// @pre The provided address must be supported by the initiator. I.e. 144 /// don't use a 10-bit address if the initiator only supports 7-bit 145 /// addresses. This method fails a runtime assertion if this precondition 146 /// isn't met. 147 /// 148 /// @returns @rst 149 /// 150 /// .. pw-status-codes:: 151 /// 152 /// OK: The transaction succeeded. 153 /// 154 /// INVALID_ARGUMENT: The device address provided is bigger than 10 bits. 155 /// 156 /// DEADLINE_EXCEEDED: Was unable to acquire exclusive initiator access 157 /// and complete the I2C transaction in time. 158 /// 159 /// UNAVAILABLE: A NACK condition occurred, meaning the addressed device 160 /// didn't respond or was unable to process the request. 161 /// 162 /// FAILED_PRECONDITION: The interface isn't initialized or enabled. 163 /// 164 /// @endrst WriteFor(Address device_address,ConstByteSpan tx_buffer,chrono::SystemClock::duration timeout)165 Status WriteFor(Address device_address, 166 ConstByteSpan tx_buffer, 167 chrono::SystemClock::duration timeout) { 168 return WriteReadFor(device_address, tx_buffer, ByteSpan(), timeout); 169 } 170 /// A variation of `pw::i2c::Initiator::WriteFor` that accepts an explicit 171 /// size for the amount of bytes to write to the device. WriteFor(Address device_address,const void * tx_buffer,size_t tx_size_bytes,chrono::SystemClock::duration timeout)172 Status WriteFor(Address device_address, 173 const void* tx_buffer, 174 size_t tx_size_bytes, 175 chrono::SystemClock::duration timeout) { 176 return WriteFor( 177 device_address, 178 span(static_cast<const std::byte*>(tx_buffer), tx_size_bytes), 179 timeout); 180 } 181 182 /// Reads bytes from an I2C device. 183 /// 184 /// The signal on the bus should look like this: 185 /// 186 /// @code 187 /// START + I2C_ADDRESS + READ(1) + RX_BUFFER_BYTES + STOP 188 /// @endcode 189 /// 190 /// @param[in] device_address The address of the I2C device. 191 /// 192 /// @param[out] rx_buffer The receive buffer. 193 /// 194 /// @param[in] timeout The maximum duration to block waiting for both 195 /// exclusive bus access and the completion of the I2C transaction. 196 /// 197 /// @pre The provided address must be supported by the initiator. I.e. 198 /// don't use a 10-bit address if the initiator only supports 7-bit 199 /// addresses. This method fails a runtime assertion if this precondition 200 /// isn't met. 201 /// 202 /// @returns @rst 203 /// 204 /// .. pw-status-codes:: 205 /// 206 /// OK: The transaction succeeded. 207 /// 208 /// INVALID_ARGUMENT: The device address provided is bigger than 10 bits. 209 /// 210 /// DEADLINE_EXCEEDED: Was unable to acquire exclusive initiator access 211 /// and complete the I2C transaction in time. 212 /// 213 /// UNAVAILABLE: A NACK condition occurred, meaning the addressed device 214 /// didn't respond or was unable to process the request. 215 /// 216 /// FAILED_PRECONDITION: The interface isn't initialized or enabled. 217 /// 218 /// @endrst ReadFor(Address device_address,ByteSpan rx_buffer,chrono::SystemClock::duration timeout)219 Status ReadFor(Address device_address, 220 ByteSpan rx_buffer, 221 chrono::SystemClock::duration timeout) { 222 return WriteReadFor(device_address, ConstByteSpan(), rx_buffer, timeout); 223 } 224 /// A variation of `pw::i2c::Initiator::ReadFor` that accepts an explicit 225 /// size for the amount of bytes to read from the device. ReadFor(Address device_address,void * rx_buffer,size_t rx_size_bytes,chrono::SystemClock::duration timeout)226 Status ReadFor(Address device_address, 227 void* rx_buffer, 228 size_t rx_size_bytes, 229 chrono::SystemClock::duration timeout) { 230 return ReadFor(device_address, 231 span(static_cast<std::byte*>(rx_buffer), rx_size_bytes), 232 timeout); 233 } 234 235 /// Probes the device for an I2C ACK after only writing the address. 236 /// This is done by attempting to read a single byte from the specified 237 /// device. 238 /// 239 /// @param[in] device_address The address of the I2C device. 240 /// 241 /// @param[in] timeout The maximum duration to block waiting for both 242 /// exclusive bus access and the completion of the I2C transaction. 243 /// 244 /// @pre The provided address must be supported by the initiator. I.e. 245 /// don't use a 10-bit address if the initiator only supports 7-bit 246 /// addresses. This method fails a runtime assertion if this precondition 247 /// isn't met. 248 /// 249 /// @returns @rst 250 /// 251 /// .. pw-status-codes:: 252 /// 253 /// OK: The transaction succeeded. 254 /// 255 /// INVALID_ARGUMENT: The device address provided is bigger than 10 bits. 256 /// 257 /// DEADLINE_EXCEEDED: Was unable to acquire exclusive initiator access 258 /// and complete the I2C transaction in time. 259 /// 260 /// UNAVAILABLE: A NACK condition occurred, meaning the addressed device 261 /// didn't respond or was unable to process the request. 262 /// 263 /// FAILED_PRECONDITION: The interface isn't initialized or enabled. 264 /// 265 /// @endrst ProbeDeviceFor(Address device_address,chrono::SystemClock::duration timeout)266 Status ProbeDeviceFor(Address device_address, 267 chrono::SystemClock::duration timeout) { 268 std::byte ignored_buffer[1] = {}; // Read a byte to probe. 269 return WriteReadFor( 270 device_address, ConstByteSpan(), ignored_buffer, timeout); 271 } 272 273 private: 274 virtual Status DoWriteReadFor(Address device_address, 275 ConstByteSpan tx_buffer, 276 ByteSpan rx_buffer, 277 chrono::SystemClock::duration timeout) = 0; 278 }; 279 280 } // namespace pw::i2c 281