diff --git a/doxyfile_options b/doxyfile_options index 05834bb..b52f409 100644 --- a/doxyfile_options +++ b/doxyfile_options @@ -571,7 +571,7 @@ # name. If set to NO, the members will appear in declaration order. # The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member diff --git a/drivers/include/drivers/I2C.h b/drivers/include/drivers/I2C.h index 030485e..b8e63b6 100644 --- a/drivers/include/drivers/I2C.h +++ b/drivers/include/drivers/I2C.h @@ -44,13 +44,34 @@ * @{ */ -/** An I2C Master, used for communicating with I2C slave devices +// Note: In the below comments, Doxygen wants to auto-link the word "I2C" to the class name. A percent sign is used to +// suppress this behavior. + +/** An %I2C Master, used for communicating with %I2C slave devices * - * @note Synchronization level: Thread safe + * There are three different forms of the %I2C API usable via this class: + * * - * Example: + * All three of these APIs let you execute %I2C operations, but they work differently. + * + * The I2C class is thread-safe, and uses a mutex to prevent multiple threads from using it at the same time. + * + *

Transaction-Based API

+ * + * The simplest API, which should be appropriate for most use cases, is the transaction-based API, which is + * accessed through the \link I2C::read(int address, char *data, int length, bool repeated) read() \endlink and the + * \link write(int address, const char *data, int length, bool repeated) write() \endlink functions. These functions + * execute an entire %I2C transaction (the start condition, address, data bytes, and stop condition) in a single + * function call. The bytes to be read/written are passed in through an array, which requires that you know the + * size of the data ahead of time. If this information is not known, you may want to use the single-byte API instead + * (see below). + * + * Example of using the transaction-based API to read the temperature from an LM75BD: * @code - * Read temperature from LM75BD * #include "mbed.h" * I2C i2c(I2C_SDA , I2C_SCL); * const int addr7bit = 0x48; // 7-bit I2C address @@ -64,51 +85,163 @@ * * // read and write takes the 8-bit version of the address. * // set up configuration register (at 0x01) - * i2c.write(addr8bit, cmd, 2); + * I2C::Result result = i2c.write(addr8bit, cmd, 2); + * + * if(result != I2C::ACK) + * { + * // Chip not accessible, handle error.... + * } * * ThisThread::sleep_for(500); * * // read temperature register * cmd[0] = 0x00; - * i2c.write(addr8bit, cmd, 1); - * i2c.read( addr8bit, cmd, 2); + * i2c.write(addr8bit, cmd, 1, true); // Set repeated to true so that we don't give up the bus after this transaction + * i2c.read(addr8bit | 1, cmd, 2); * * float tmp = (float((cmd[0]<<8)|cmd[1]) / 256.0); * printf("Temp = %.2f\n", tmp); * } * } * @endcode + * + * + * + *

Single-Byte API

+ * + * The single-byte API consists of the \link I2C::start() start() \endlink, \link I2C::write_byte() write_byte() + * \endlink, \link I2C::read_byte() read_byte() \endlink, and \link I2C::stop() stop() \endlink functions. + * With the single-byte API, you have manual control over each condition and data byte put onto the I2C bus. + * This is useful for dealing with devices which can return variable amounts of data in one I2C operation, + * or when you don't want to create buffers to store the data. However, this API is more verbose than the + * transaction-based API and will have a bit more overhead since there's more code executing per byte. + * + * The following is an example that accomplishes the same thing as the above code, but using the single-byte API. + * @code + * #include "mbed.h" + * I2C i2c(I2C_SDA , I2C_SCL); + * const int addr7bit = 0x48; // 7-bit I2C address + * const int addr8bit = 0x48 << 1; // 8-bit I2C address, 0x90 + * + * int main() { + * while (1) { + * // read and write takes the 8-bit version of the address. + * // set up configuration register (at 0x01) + * i2c.start(); + * I2C::Result result = i2c.write_byte(addr8bit); // Write address, LSBit low to indicate write + * i2c.write_byte(0x01); + * i2c.write_byte(0x00); + * i2c.stop(); + * + * if(result != I2C::ACK) + * { + * // Chip not accessible, handle error.... + * } + * + * ThisThread::sleep_for(500); + * + * // Set register to read + * i2c.start(); + * i2c.write_byte(addr8bit); // Write address + * i2c.write_byte(0x00); + * // To create a repeated start condition, we do not call stop() here + * + * i2c.start(); + * i2c.write_byte(addr8bit | 1); // Write address, LSBit high to indicate read + * + * // Read the two byte temperature word + * uint16_t temperatureBinary = 0; + * temperatureBinary |= static_cast(i2c.read_byte()) << 8; + * temperatureBinary |= static_cast(i2c.read_byte()); + * + * float tmp = (float(temperatureBinary) / 256.0); + * printf("Temp = %.2f\n", tmp); + * } + * } + * @endcode + * + * \attention If a single I2C object is being shared among multiple threads, you should surround usage of the + * single-byte API with \link I2C::lock() lock() \endlink and \link I2C::unlock() unlock() \endlink. This + * ensures that a transaction by one thread is not interrupted by another. It may also improve performance + * because the backing mutex will not need to be locked for each byte. + * + *

Asynchronous API

+ * + * The asynchronous API allows you to run %I2C operations in the background. This API is only + * available if your device has the I2C_ASYNCH feature. To use this API, use \link I2C::transfer() transfer() \endlink + * to start an operation and \link I2C::abort_transfer() abort_transfer() \endlink to stop it. Alternately, use the + * \link I2C::transfer_and_wait() transfer_and_wait() \endlink function to block the current thread until + * the transfer finishes. + * + * Some devices implement these features using DMA, others use interrupts, so be mindful that there may still be + * significant CPU usage if you have multiple and/or high-rate transfers going on. + * + *

A Note about Addressing

+ * Most %I2C devices make use of 7-bit addresses (see here for details). + * Mbed OS, however, works with addresses in 8-bit format, where the least significant bit specifies if the transaction + * is a read (1) or a write (0). Due to this, you will generally need to use bitshifts and bitwise ORs when passing + * addresses to I2C functions. See the documentation on each function for details. + * + * %I2C also has a 10-bit addressing mode, where + * the address is sent in two logical bytes on the bus. Some, but not all, Mbed targets support this mode -- refer + * to your MCU datasheet and your target's HAL code for details. For 10-bit addresses, use the same format to + * pass them to I2C functions -- shift them left by one and set the LSBit to indicate the read/write direction. + * On MCUs that do not natively support 10-bit addressing, you can emulate support by using the single-byte API + * to send two address bytes; see the linked page above for details. + * + *

Other Info

+ * + * \warning Mbed OS requires that you only create one instance of the I2C class per physical %I2C bus on your chip. + * This means that if you have multiple sensors connected together on a bus, you must create one I2C object at the + * top level and pass it in to the drivers for each sensor. Violating this directive will cause undefined + * behavior in your code. + * + * \attention Due to how %I2C works, if multiple devices are sharing a bus which support different %I2C speeds, you cannot + * go faster than the maximum bus speed of any of the devices. Otherwise, slower devices may misinterpret messages + * that are too fast for them and cause interference on the bus. For example, if you have two 400kHz devices and one + * 100kHz device on a bus, you must run the entire bus at 100kHz! */ class I2C : private NonCopyable { public: - enum RxStatus { - NoData, - MasterGeneralCall, - MasterWrite, - MasterRead + + /** + * Result code for I2C operations + */ + enum Result : int { + /// ACK was received + ACK = 0, + /// NACK was received + NACK, + /// Timeout waiting for I2C hardware + TIMEOUT, + /// Other error in I2C operation + OTHER_ERROR }; - enum Acknowledge { - NoACK = 0, - ACK = 1 - }; - /** Create an I2C Master interface, connected to the specified pins + /** Create an I2C Master interface, connected to the specified pins. + * The new object defaults to 100kHz speed. * * @param sda I2C data line pin * @param scl I2C clock line pin */ I2C(PinName sda, PinName scl); - /** Create an I2C Master interface, connected to the specified pins + /** Create an I2C Master interface, connected to the specified pins. + * The new object defaults to 100kHz speed. * * @param static_pinmap reference to structure which holds static pinmap. */ I2C(const i2c_pinmap_t &static_pinmap); I2C(const i2c_pinmap_t &&) = delete; // prevent passing of temporary objects - /** Set the frequency of the I2C interface + /** Set the frequency of the I2C interface. + * If you do not call this function, the I2C will run at 100kHz speed. + * + * Note: Some underlying HALs only support a very limited set of common I2C frequencies, such as 100kHz and + * 400kHz. Other implementations support all frequencies. If the frequency you set is not supported, you will get + * an assertion failure after calling this function. * * @param hz The bus frequency in hertz */ @@ -116,48 +249,91 @@ /** Read from an I2C slave * - * Performs a complete read transaction. The bottom bit of - * the address is forced to 1 to indicate a read. + * Performs a complete read transaction. The least significant bit of + * the address must be 1 to indicate a read. * - * @param address 8-bit I2C slave address [ addr | 1 ] + * @param address 8/11-bit I2C slave address [ (7 or 10 bit addr << 1) | 1 ] * @param data Pointer to the byte-array to read data in to * @param length Number of bytes to read - * @param repeated Repeated start, true - don't send stop at end - * default value is false. + * @param repeated Set up for a repeated start. If true, the Mbed processor does not relinquish the bus after + * this read operation. You may then call write(), read(), or start() again to start another operation. + * + * @returns Result enum describing whether the I2C transaction succeeded or failed + */ + Result read(int address, char *data, int length, bool repeated = false); + + /** Write to an I2C slave + * + * Performs a complete write transaction. The least significant bit of + * the address must be 0 to indicate a write. + * + * @param address 8/11-bit I2C slave address [ (7 or 10 bit addr << 1) | 0 ] + * @param data Pointer to the byte-array data to send + * @param length Number of bytes to send + * @param repeated Set up for a repeated start. If true, the Mbed processor does not relinquish the bus after + * this write operation. You may then call write(), read(), or start() again to start another operation. + * + * @returns Result enum describing whether the I2C transaction succeeded or failed + */ + Result write(int address, const char *data, int length, bool repeated = false); + + /** Creates a start condition on the %I2C bus. After calling this function, you should call + * \link write_byte() \endlink to send the %I2C address. + */ + void start(void); + + /** Read a single byte from the %I2C bus. + * + * After calling this function, you may call it again to read another byte from the slave. Alternately, + * you may call \link stop() \endlink to stop the current transaction, or \link start() \endlink to + * start a new transaction. + * + * Note: Reads are not acknowledged by the slave device in I2C, which is why this function does not + * return an ACK/NACK result. + * + * @param ack indicates if the byte is to be acknowledged (true = acknowledge). Use false to indicate to + * the slave that you don't want to read any more data. * * @returns - * 0 on success (ack), - * nonzero on failure (nack) + * the byte read, or -1 on error. */ - int read(int address, char *data, int length, bool repeated = false); + int read_byte(bool ack); - /** Read a single byte from the I2C bus + /** Read a single byte from the %I2C bus. This function is a legacy alias for \link read_byte() \endlink + * + * After calling this function, you may call it again to read another byte from the slave. Alternately, + * you may call \link stop() \endlink to stop the current transaction, or \link start() \endlink to + * start a new transaction. + * + * Note: Reads are not acknowledged by the slave device in I2C, which is why this function does not + * return an ACK/NACK result. * * @param ack indicates if the byte is to be acknowledged (1 = acknowledge) * * @returns * the byte read */ - int read(int ack); + int read(int ack) + { + return read_byte(ack); + } - /** Write to an I2C slave + /** Write a single byte out on the %I2C bus. The very first write_byte() call after calling start() + * is used to set up the slave address. * - * Performs a complete write transaction. The bottom bit of - * the address is forced to 0 to indicate a write. + * After calling this function, you may call \link write_byte() \endlink again to write bytes in a write operation, + * or \link read_byte() \endlink to read bytes in a read operation. Once done, call \link stop() \endlink + * to stop the current transaction or \link start() \endlink to start a new transaction. * - * @param address 8-bit I2C slave address [ addr | 0 ] - * @param data Pointer to the byte-array data to send - * @param length Number of bytes to send - * @param repeated Repeated start, true - do not send stop at end - * default value is false. + * @param data data to write out on bus. Note: This is an int, not a uint8_t, to support addressing modes + * with more than 7 bits. * - * @returns - * 0 on success (ack), - * nonzero on failure (nack) + * @returns Result enum describing whether the I2C byte was acknowledged or not */ - int write(int address, const char *data, int length, bool repeated = false); + Result write_byte(int data); - /** Write single byte out on the I2C bus + /** Write a single byte out on the %I2C bus. Deprecated version of \link write_byte() \endlink, with a legacy + * return code format. * * @param data data to write out on bus * @@ -166,21 +342,20 @@ * '1' - ACK was received, * '2' - timeout */ + MBED_DEPRECATED_SINCE("mbed-ce", "Use I2C::write_byte() instead for better readability and return codes") int write(int data); - /** Creates a start condition on the I2C bus - */ - void start(void); - - /** Creates a stop condition on the I2C bus + /** + * Creates a stop condition on the %I2C bus. This puts the bus back into an idle state where new transactions can be + * initiated by this device or others. */ void stop(void); - /** Acquire exclusive access to this I2C bus + /** Acquire exclusive access to this %I2C bus */ virtual void lock(void); - /** Release exclusive access to this I2C bus + /** Release exclusive access to this %I2C bus */ virtual void unlock(void); @@ -191,19 +366,34 @@ #if DEVICE_I2C_ASYNCH - /** Start nonblocking I2C transfer. + /** Start nonblocking %I2C transfer. * - * This function locks the deep sleep until any event has occurred + * The %I2C peripheral will begin a transmit and/or receive operation in the background. If only a transmit + * or receive buffer is specified, only a transmit or receive will be done. If both buffers are specified, + * first the transmission is done to the given slave address, then the specified number of bytes are received. * - * @param address 8/10 bit I2C slave address - * @param tx_buffer The TX buffer with data to be transferred - * @param tx_length The length of TX buffer in bytes - * @param rx_buffer The RX buffer, which is used for received data - * @param rx_length The length of RX buffer in bytes - * @param event The logical OR of events to modify + * If you wish to find out when the transfer is done, pass a callback function to the callback argument + * and set the event argument to the events you wish to receive. + * This callback will be called when the transfer completes or errors out. Be careful: if you + * only request the I2C_EVENT_TRANSFER_COMPLETE event, and the transfer errors, the callback will never be called. + * + * Internally, the chip vendor may implement this function using either DMA or interrupts. + * + * This function locks the deep sleep until any event has occurred. + * + * You may not call any other functions on this class instance until the transfer is complete, has errored, + * or is aborted. Trying to start multiple transfers at once will return an error. + * + * @param address 8/11 bit %I2C slave address + * @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0. + * @param tx_length The length of TX buffer in bytes. If 0, no transmission is done. + * @param rx_buffer The RX buffer, which is used for received data. May be nullptr if tx_length is 0. + * @param rx_length The length of RX buffer in bytes If 0, no reception is done. + * @param event The logical OR of events to modify. May be I2C_EVENT_ALL, or some combination + * of the flags I2C_EVENT_ERROR, I2C_EVENT_ERROR_NO_SLAVE, I2C_EVENT_TRANSFER_COMPLETE, or I2C_EVENT_TRANSFER_EARLY_NACK * @param callback The event callback function - * @param repeated Repeated start, true - do not send stop at end - * default value is false. + * @param repeated Set up for a repeated start. If true, the Mbed processor does not relinquish the bus after + * this write operation. You may then call write(), read(), or start() again to start another operation. * * @returns Zero if the transfer has started, or -1 if I2C peripheral is busy */ @@ -213,6 +403,30 @@ */ void abort_transfer(); + /** Start %I2C transfer and wait until it is complete. Like the transactional API this blocks the current thread, + * however all work is done in the background and other threads may execute. + * + * The %I2C peripheral will begin a transmit and/or receive operation in the background. If only a transmit + * or receive buffer is specified, only a transmit or receive will be done. If both buffers are specified, + * first the transmission is done to the given slave address, then the specified number of bytes are received. + * + * Internally, the chip vendor may implement this function using either DMA or interrupts. + * + * This function locks the deep sleep until it returns. + * + * @param address 8/11 bit %I2C slave address + * @param tx_buffer The TX buffer with data to be transferred. May be nullptr if tx_length is 0. + * @param tx_length The length of TX buffer in bytes. If 0, no transmission is done. + * @param rx_buffer The RX buffer, which is used for received data. May be nullptr if tx_length is 0. + * @param rx_length The length of RX buffer in bytes If 0, no reception is done. + * @param timeout timeout value. Use @link rtos::Kernel::wait_for_u32_forever to wait forever (the default). + * @param repeated Set up for a repeated start. If true, the Mbed processor does not relinquish the bus after + * this operation. You may then call write(), read(), or start() again to start another operation. + * + * @returns Result code describing whether the transfer succeeded or not. + */ + Result transfer_and_wait(int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout = rtos::Kernel::wait_for_u32_forever, bool repeated = false); + #if !defined(DOXYGEN_ONLY) protected: /** Lock deep sleep only if it is not yet locked */ diff --git a/drivers/source/I2C.cpp b/drivers/source/I2C.cpp index ae03c99..3718e8b 100644 --- a/drivers/source/I2C.cpp +++ b/drivers/source/I2C.cpp @@ -23,6 +23,7 @@ #if DEVICE_I2C_ASYNCH #include "platform/mbed_power_mgmt.h" +#include "rtos/EventFlags.h" #endif namespace mbed { @@ -69,7 +70,7 @@ } // write - Master Transmitter Mode -int I2C::write(int address, const char *data, int length, bool repeated) +I2C::Result I2C::write(int address, const char *data, int length, bool repeated) { lock(); @@ -77,19 +78,13 @@ int written = i2c_write(&_i2c, address, data, length, stop); unlock(); - return length != written; -} -int I2C::write(int data) -{ - lock(); - int ret = i2c_byte_write(&_i2c, data); - unlock(); - return ret; + // Note: C i2c_write() function does not distinguish between NACKs and errors, so assume NACK if read did not go through + return length == written ? Result::ACK : Result::NACK; } // read - Master Receiver Mode -int I2C::read(int address, char *data, int length, bool repeated) +I2C::Result I2C::read(int address, char *data, int length, bool repeated) { lock(); @@ -97,10 +92,19 @@ int read = i2c_read(&_i2c, address, data, length, stop); unlock(); - return length != read; + + // Note: C i2c_read() function does not distinguish between NACKs and errors, so assume NACK if read did not go through + return length == read ? Result::ACK : Result::NACK; } -int I2C::read(int ack) +void I2C::start(void) +{ + lock(); + i2c_start(&_i2c); + unlock(); +} + +int I2C::read_byte(bool ack) { lock(); int ret; @@ -113,11 +117,39 @@ return ret; } -void I2C::start(void) +I2C::Result I2C::write_byte(int data) { lock(); - i2c_start(&_i2c); + int ret = i2c_byte_write(&_i2c, data); unlock(); + + switch (ret) { + case 0: + return Result::NACK; + case 1: + return Result::ACK; + case 2: + return Result::TIMEOUT; + default: + return Result::OTHER_ERROR; + } +} + +int I2C::write(int data) +{ + auto result = write_byte(data); + + // Replicate the legacy return code + switch (result) { + case Result::ACK: + return 1; + case Result::NACK: + return 0; + case Result::TIMEOUT: + return 2; + default: + return static_cast(result); + } } void I2C::stop(void) @@ -210,6 +242,49 @@ unlock(); } +I2C::Result I2C::transfer_and_wait(int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout, bool repeated) +{ + // Use EventFlags to suspend the thread until the transfer finishes + rtos::EventFlags transferResultFlags("I2C::Result EvFlags"); + + // Simple callback from the transfer that sets the EventFlags using the I2C result event + event_callback_t transferCallback([&](int event) { + transferResultFlags.set(event); + }); + + transfer(address, tx_buffer, tx_length, rx_buffer, rx_length, transferCallback, I2C_EVENT_ALL, repeated); + + // Wait until transfer complete, error, or timeout + uint32_t result = transferResultFlags.wait_any_for(I2C_EVENT_ALL, timeout); + + if (result & osFlagsError) { + if (result == osFlagsErrorTimeout) { + // Timeout expired, cancel transfer. + abort_transfer(); + return Result::TIMEOUT; + } else { + // Other event flags error. Transfer might be still running so cancel it. + abort_transfer(); + return Result::OTHER_ERROR; + } + } else { + // Note: Cannot use a switch here because multiple flags might be set at the same time (possible + // in the STM32 HAL code at least). + if (result & I2C_EVENT_TRANSFER_COMPLETE) { + return Result::ACK; + } else if ((result & I2C_EVENT_ERROR_NO_SLAVE) || (result & I2C_EVENT_TRANSFER_EARLY_NACK)) { + // Both of these events mean that a NACK was received somewhere. Theoretically NO_SLAVE means + // NACK while transmitting address and EARLY_NACK means nack during the write operation. + // But these aren't distinguished in the Result enum and even some of the HALs treat them + // interchangeably. + return Result::NACK; + } else { + // Other / unknown error code + return Result::OTHER_ERROR; + } + } +} + void I2C::irq_handler_asynch(void) { int event = i2c_irq_handler_asynch(&_i2c); diff --git a/hal/include/hal/i2c_api.h b/hal/include/hal/i2c_api.h index 9a71985..f6b89e8 100644 --- a/hal/include/hal/i2c_api.h +++ b/hal/include/hal/i2c_api.h @@ -190,7 +190,7 @@ /** Blocking reading data * * @param obj The I2C object - * @param address 7-bit address (last bit is 1) + * @param address 8/11-bit address (last bit is 1) * @param data The buffer for receiving * @param length Number of bytes to read * @param stop Stop to be generated after the transfer is done @@ -201,7 +201,7 @@ /** Blocking sending data * * @param obj The I2C object - * @param address 7-bit address (last bit is 0) + * @param address 8/11-bit address (last bit is 0) * @param data The buffer for sending * @param length Number of bytes to write * @param stop Stop to be generated after the transfer is done @@ -334,7 +334,7 @@ * @param tx_length The number of bytes to transmit * @param rx The receive buffer * @param rx_length The number of bytes to receive - * @param address The address to be set - 7bit or 9bit + * @param address The address to be set - 8bit or 11bit * @param stop If true, stop will be generated after the transfer is done * @param handler The I2C IRQ handler to be set * @param event Event mask for the transfer. See \ref hal_I2CEvents diff --git a/rtos/include/rtos/EventFlags.h b/rtos/include/rtos/EventFlags.h index 621e059..d1b4ee8 100644 --- a/rtos/include/rtos/EventFlags.h +++ b/rtos/include/rtos/EventFlags.h @@ -101,7 +101,7 @@ /** Wait for all of the specified event flags to become signaled. @param flags the flags to wait for. - @param rel_time timeout value. + @param rel_time timeout value. Use \link Kernel::wait_for_u32_forever \endlink to wait forever. @param clear clear specified event flags after waiting for them (default: true). @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). @@ -131,7 +131,7 @@ /** Wait for any of the specified event flags to become signaled. @param flags the flags to wait for. - @param rel_time timeout value. + @param rel_time timeout value. Use \link Kernel::wait_for_u32_forever \endlink to wait forever. @param clear clear specified event flags after waiting for them (default: true). @return event flags before clearing or error code if highest bit set (see @a osFlagsError for details). diff --git a/storage/blockdevice/COMPONENT_I2CEE/source/I2CEEBlockDevice.cpp b/storage/blockdevice/COMPONENT_I2CEE/source/I2CEEBlockDevice.cpp index 9f3fbf8..10537d0 100644 --- a/storage/blockdevice/COMPONENT_I2CEE/source/I2CEEBlockDevice.cpp +++ b/storage/blockdevice/COMPONENT_I2CEE/source/I2CEEBlockDevice.cpp @@ -73,21 +73,25 @@ _i2c->start(); - if (1 != _i2c->write(get_paged_device_address(addr))) { + if (I2C::ACK != _i2c->write_byte(get_paged_device_address(addr))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } - if (!_address_is_eight_bit && 1 != _i2c->write((char)(addr >> 8u))) { + if (!_address_is_eight_bit && I2C::ACK != _i2c->write_byte((char)(addr >> 8u))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } - if (1 != _i2c->write((char)(addr & 0xffu))) { + if (I2C::ACK != _i2c->write_byte((char)(addr & 0xffu))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } - _i2c->stop(); + // Note: We do not send an I2C stop in this case, because we will do a repeated start in the next + // call. - if (0 != _i2c->read(_i2c_addr, pBuffer, size)) { + if (I2C::ACK != _i2c->read(_i2c_addr | 1, pBuffer, static_cast(size))) { return BD_ERROR_DEVICE_ERROR; } @@ -108,20 +112,24 @@ _i2c->start(); - if (1 != _i2c->write(get_paged_device_address(addr))) { + if (I2C::ACK != _i2c->write_byte(get_paged_device_address(addr))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } - if (!_address_is_eight_bit && 1 != _i2c->write((char)(addr >> 8u))) { + if (!_address_is_eight_bit && I2C::ACK != _i2c->write_byte((char)(addr >> 8u))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } - if (1 != _i2c->write((char)(addr & 0xffu))) { + if (I2C::ACK != _i2c->write_byte((char)(addr & 0xffu))) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } for (unsigned i = 0; i < chunk; i++) { - if (1 != _i2c->write(pBuffer[i])) { + if (I2C::ACK != _i2c->write_byte(pBuffer[i])) { + _i2c->stop(); return BD_ERROR_DEVICE_ERROR; } } @@ -154,7 +162,7 @@ // so loop trying to do a zero byte write until it is ACKed // by the chip. for (int i = 0; i < I2CEE_TIMEOUT; i++) { - if (_i2c->write(_i2c_addr | 0, 0, 0) < 1) { + if (_i2c->write(_i2c_addr | 0, 0, 0) == I2C::ACK) { return 0; } wait_us(100); diff --git a/tools/test/ci/doxy-spellchecker/ignore.en.pws b/tools/test/ci/doxy-spellchecker/ignore.en.pws index fbc768b..77ee02b 100644 --- a/tools/test/ci/doxy-spellchecker/ignore.en.pws +++ b/tools/test/ci/doxy-spellchecker/ignore.en.pws @@ -123,3 +123,6 @@ _doxy_ nothrow conf +transactional +errored +natively