mbed-os/hal/i2c_api.h

531 lines
22 KiB
C

/* mbed Microcontroller Library
* Copyright (c) 2006-2019 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** \addtogroup hal */
/** @{*/
#ifndef MBED_I2C_API_H
#define MBED_I2C_API_H
#include "device.h"
#include "pinmap.h"
#include "hal/buffer.h"
#if DEVICE_I2C_ASYNCH
#include "hal/dma_api.h"
#endif
#include <stdbool.h>
#if DEVICE_I2C
/**
* \defgroup hal_i2c I2C hal
*
* The I2C hal provides a low level interface to the I2C interface of a target.
*
* # Defined behavior
* * The function ::i2c_init initializes the peripheral pins specified in the input parameters,
* initializes the peripheral in master mode if `is_slave` is false,
* initializes the peripheral in slave mode if `is_slave` is true and `supports_slave_mode` is true - Verified by test ::fpga_i2c_test_init_free.
* * The function ::i2c_free resets the pins used to initialize the peripheral to their default state and
* disables the peripheral clock - Verified by test ::fpga_i2c_test_init_free.
* * The function ::i2c_get_capabilities fills the contents of the `i2c_capabilities_t` parameter - Verified by test ::fpga_i2c_test_get_capabilities.
* * The function ::i2c_frequency sets the frequency to use for the transfer, returns the actual frequency used and
* must leave all other configuration unchanged - Verified by test ::fpga_i2c_test_frequency.
* * The function ::i2c_timeout sets the transmision timeout to use for the following blocking transfers.
* If the timeout is not set, the default timeout is used.
* The default timeout value is based on I2C frequency. It's computed as triple the amount of time it would take to send data over I2C - Verified by test ::fpga_i2c_test_blocking_transmission_timeout.
* * The function ::i2c_write writes `length` number of symbols to the bus, returns the number of symbols sent to the bus,
* returns an error code if the transfer fails, generates a stop condition on the bus at the end of the transfer if `stop` parameter is true,
* and handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware.
* The transfer times out and returns `I2C_ERROR_TIMEOUT ` if the transfer takes longer than the configured timeout duration - Verified by test ::fpga_i2c_test_blocking_write_read.
* * The function ::i2c_read reads `length` symbols from the bus, returns the number of symbols received from the bus,
* returns an error code if the transfer fails, generates a stop condition on the bus at the end of the transfer if `stop` parameter is true
* and handles transfer collisions and loss of arbitration if the platform supports multimaster in hardware.
* The transfer times out and returns `I2C_ERROR_TIMEOUT ` if the transfer takes longer than the configured timeout duration - Verified by test ::fpga_i2c_test_blocking_write_read.
* * The function ::i2c_start generates I2C START condition on the bus in master mode and does nothing if called when the peripheral is configured in slave mode.
* * The function ::i2c_stop generates I2C STOP condition on the bus in master mode and does nothing if called when the peripheral is configured in slave mode
* - Verified by test ::fpga_i2c_test_blocking_write_read ::fpga_i2c_test_async_write_read.
* * The function ::i2c_slave_status indicates which mode the peripheral has been addressed in and returns not addressed when called in master mode.
* * The function ::i2c_slave_address sets the address of the peripheral to the `address` parameter and does nothing if called in master mode.
* * The function ::i2c_transfer_async returns immediately with a `bool` indicating whether the transfer was successfully scheduled.
* The callback given to `i2c_transfer_async` is invoked when the transfer finishes or an error occurs and
* must save the handler and context pointers inside the `obj` pointer. The context pointer is passed to the callback on transfer completion.
* The callback must be invoked on completion unless the transfer is aborted and
* may handle transfer collisions and loss of arbitration if the platform supports multimaster in hardware and enabled in API.
* `i2c_async_event_t` must be filled with the number of symbols sent to the bus during transfer - Verified by test ::fpga_i2c_test_async_write_read.
* * The function ::i2c_abort_async aborts any ongoing async transfers - Verified by test ::fpga_i2c_test_async_abort.
*
* # Undefined behaviors
*
* * Use of a `null` pointer as an argument to any function.
* * Calling any `I2C` function before calling ::i2c_init or after calling ::i2c_free.
* * Initializing the `I2C` peripheral with invalid `SDA` and `SCL` pins.
* * Initializing the peripheral in slave mode if slave mode is not supported, indicated by ::i2c_get_capabilities.
* * Operating the peripheral in slave mode without first specifying an address using ::i2c_slave_address.
* * Setting an address using i2c_slave_address after initializing the peripheral in master mode.
* * Setting an address to an `I2C` reserved value.
* * Setting an address larger than the 7-bit supported maximum if 10-bit addressing is not supported.
* * Setting an address larger than the 10-bit supported maximum.
* * Setting a frequency outside the supported range given by ::i2c_get_capabilities.
* * Using the device in a multimaster configuration when `supports_multimaster_mode` is false.
* * Specifying an invalid address when calling any `read` or `write` functions.
* * Setting the length of the transfer or receive buffers to larger than the buffers are.
* * Passing an invalid pointer as `handler` to ::i2c_transfer_async.
* * Calling ::i2c_abort_async when no transfer is in progress.
*
* @{
*/
/**
* \defgroup hal_i2c_tests I2C hal tests
* The I2C test validate proper implementation of the I2C hal.
*
* To run the I2C hal tests, use the command:
*
* mbed test -t <toolchain> -m <target> -n tests-mbed_hal_fpga_ci_test_shield-i2c
*/
/**
* @defgroup hal_I2CEvents I2C Events Macros
*
* @{
*/
#define I2C_EVENT_ERROR (1 << 1)
#define I2C_EVENT_ERROR_NO_SLAVE (1 << 2)
#define I2C_EVENT_TRANSFER_COMPLETE (1 << 3)
#define I2C_EVENT_TRANSFER_EARLY_NACK (1 << 4)
#define I2C_EVENT_ARBITRATION_LOST (1 << 5)
#define I2C_EVENT_ALL (I2C_EVENT_ERROR | \
I2C_EVENT_TRANSFER_COMPLETE | \
I2C_EVENT_ERROR_NO_SLAVE | \
I2C_EVENT_TRANSFER_EARLY_NACK | \
I2C_EVENT_ARBITRATION_LOST)
/**@}*/
typedef struct i2c i2c_t;
#if DEVICE_I2C_ASYNCH
/** Structure describing the status of async transfer */
typedef struct i2c_async_event {
uint32_t sent_bytes;
uint32_t received_bytes;
int32_t error_status; // error description I2C_ERROR_XXX
bool error;
} i2c_async_event_t;
/** Asynchronous transfer callback.
*
* @param obj The I2C object
* @param event Pointer to the event holding async transfer status
* @param ctx The context pointer
*
* @note Callback is invoked when async transfer completes or when an error is detected.
*/
typedef void (*i2c_async_handler_f)(i2c_t *obj, i2c_async_event_t *event, void *ctx);
#endif
/** I2C HAL structure */
struct i2c {
struct i2c_s i2c; /**< Target specific I2C structure */
#if DEVICE_I2C_ASYNCH
struct buffer_s tx_buff; /**< Tx buffer */
struct buffer_s rx_buff; /**< Rx buffer */
i2c_async_handler_f handler;
void *ctx;
#endif
};
/** Transmission error codes */
enum {
I2C_ERROR_NO_SLAVE = -1,
I2C_ERROR_BUS_BUSY = -2,
I2C_ERROR_TIMEOUT = -3,
I2C_ERROR_ARBITRATION_LOST = -4
};
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
/**< Minimum frequency supported must be set by target device */
uint32_t minimum_frequency;
/**< Maximum frequency supported must be set by target device */
uint32_t maximum_frequency;
/**< If true, the device can handle I2C slave mode. */
bool supports_slave_mode;
/**< If true, supports 10-bit addressing. */
bool supports_10bit_addressing;
/**< If true, the device handle multimaster collisions and arbitration safely*/
bool supports_multi_master;
/**< If true, supports configuring clock stretching. */
bool supports_clock_stretching;
} i2c_capabilities_t;
/**
* \defgroup hal_GeneralI2C I2C Configuration Functions
*
* # Defined behavior
* * ::i2c_init initializes i2c_t control structure
* * ::i2c_init configures the pins used by I2C
* * ::i2c_free returns the pins owned by the I2C object to their reset state
* * ::i2c_frequency configure the I2C frequency
* * ::i2c_start sends START command
* * ::i2c_read reads `length` bytes from the I2C slave specified by `address` to the `data` buffer
* * ::i2c_read reads generates a stop condition on the bus at the end of the transfer if `stop` parameter is non-zero
* * ::i2c_read reads returns the number of symbols received from the bus
* * ::i2c_write writes `length` bytes to the I2C slave specified by `address` from the `data` buffer
* * ::i2c_write generates a stop condition on the bus at the end of the transfer if `stop` parameter is non-zero
* * ::i2c_write returns zero on success, error code otherwise
* * ::i2c_reset resets the I2C peripheral
* * ::i2c_byte_read reads and return one byte from the specfied I2C slave
* * ::i2c_byte_read uses `last` parameter to inform the slave that all bytes have been read
* * ::i2c_byte_write writes one byte to the specified I2C slave
* * ::i2c_byte_write returns 0 if NAK was received, 1 if ACK was received, 2 for timeout
* * ::i2c_slave_mode enables/disables I2S slave mode
* * ::i2c_slave_receive returns: 1 - read addresses, 2 - write to all slaves, 3 write addressed, 0 - the slave has not been addressed
* * ::i2c_slave_read reads `length` bytes from the I2C master to the `data` buffer
* * ::i2c_slave_read returns non-zero if a value is available, 0 otherwise
* * ::i2c_slave_write writes `length` bytes to the I2C master from the `data` buffer
* * ::i2c_slave_write returns non-zero if a value is available, 0 otherwise
* * ::i2c_slave_address configures I2C slave address
* * ::i2c_transfer_asynch starts I2C asynchronous transfer
* * ::i2c_transfer_asynch writes `tx_length` bytes to the I2C slave specified by `address` from the `tx` buffer
* * ::i2c_transfer_asynch reads `rx_length` bytes from the I2C slave specified by `address` to the `rx` buffer
* * ::i2c_transfer_asynch generates a stop condition on the bus at the end of the transfer if `stop` parameter is non-zero
* * The callback given to ::i2c_transfer_asynch is invoked when the transfer completes
* * ::i2c_transfer_asynch specifies the logical OR of events to be registered
* * The ::i2c_transfer_asynch function may use the `DMAUsage` hint to select the appropriate async algorithm
* * ::i2c_irq_handler_asynch returns event flags if a transfer termination condition was met, otherwise returns 0.
* * ::i2c_active returns non-zero if the I2C module is active or 0 if it is not
* * ::i2c_abort_asynch aborts an on-going async transfer
*
* # Undefined behavior
* * Calling ::i2c_init multiple times on the same `i2c_t`
* * Calling any function other than ::i2c_init on a non-initialized `i2c_t`
* * Initialising the `I2C` peripheral with invalid `SDA` and `SCL` pins.
* * Passing pins that cannot be on the same peripheral
* * Passing an invalid pointer as `obj` to any function
* * Use of a `null` pointer as an argument to any function.
* * Initialising the peripheral in slave mode if slave mode is not supported
* * Operating the peripheral in slave mode without first specifying and address using ::i2c_slave_address
* * Setting an address using i2c_slave_address after initialising the peripheral in master mode
* * Setting an address to an `I2C` reserved value
* * Specifying an invalid address when calling any `read` or `write` functions
* * Setting the length of the transfer or receive buffers to larger than the buffers are
* * Passing an invalid pointer as `handler`
* * Calling ::i2c_abort_async when no transfer is currently in progress
*
*
* @{
*/
/**
* \defgroup hal_GeneralI2C_tests I2C hal tests
* The I2C HAL tests ensure driver conformance to defined behaviour.
*
* To run the I2C hal tests use the command:
*
* mbed test -t <toolchain> -m <target> -n tests-mbed_hal_fpga_ci_test_shield-i2c
*
*/
/** Fills structure indicating supported features and frequencies on the current
* platform.
*
* @param[out] capabilities Capabilities structure filled with supported
* configurations.
*/
void i2c_get_capabilities(i2c_capabilities_t *capabilities);
/** Initialize the I2C peripheral. It sets the default parameters for the I2C
* peripheral and configures its pins.
*
* @param obj The I2C object
* @param sda The sda pin
* @param scl The scl pin
* @param is_slave Choose whether the peripheral is initialized as master or
* slave.
*/
void i2c_init(i2c_t *obj, PinName sda, PinName scl, bool is_slave);
/** Release the I2C object.
*
* @param obj The I2C object to deinitialize
*/
void i2c_free(i2c_t *obj);
/** Configure the frequency in Hz at which the I2C peripheral should operate.
*
* @param obj The I2C object
* @param frequency Frequency in Hz
*
* @returns The actual frequency that the peripheral generates to
* allow a user to adjust its strategy in case the target cannot be
* reached.
*/
uint32_t i2c_frequency(i2c_t *obj, uint32_t frequency);
/** Enable or disable clock stretching for the I2C peripheral.
*
* @param obj The I2C object
* @param enabled If 'true', enable clock stretching on the given I2C peripheral;
* otherwise, disable it.
*/
void i2c_set_clock_stretching(i2c_t *obj, bool enabled);
/** Configure the timeout duration in microseconds for blocking transmission
*
* @param obj The I2C object
* @param timeout Transmission timeout in microseconds.
*
* @note If no timeout is set, the default timeout is used.
* Default timeout value is based on I2C frequency.
* Byte timeout is computed as triple amount of time it would take
* to send 10 bit over I2C and is expressed by the formula:
* byte_timeout = 3 * (1/frequency * 10 * 1000000)
*/
void i2c_timeout(i2c_t *obj, uint32_t timeout);
/** Send START command
*
* @param obj The I2C object.
*/
void i2c_start(i2c_t *obj);
/** Send STOP command
*
* @param obj The I2C object
*/
void i2c_stop(i2c_t *obj);
/** Blocking sending data
*
* This function transmits data when the peripheral is configured as Master to
* the selected slave and when configured as Slave transmits data to the
* Master.
*
* This function is blocking; it returns when the transfer is complete or a
* timeout event is triggered. The number of bytes transmitted is returned by
* the function after the operation is completed. Transmit operation cannot be
* canceled or aborted.
*
* The data buffer must stay allocated during the duration of the transfer, and
* the contents must not be modified. The value of the specified `address` is
* ignored when configured in slave mode. In master mode, it contains the
* address of the target peripheral. This is a 7-bit value unless 10-bit
* addressing is configured, and the target supports it.
*
* When in master mode, the operation consists of:
* - Addressing the slave as a Master transmitter.
* - Transmitting data to the addressed slave.
* - Generating a STOP condition if the specified `stop` field is true.
*
* @param obj The I2C object
* @param address 7/10-bit address (last bit is 0)
* @param data The buffer for sending
* @param length Number of bytes to write
* @param stop If true, stop is generated after the transfer is done
*
* @note If the current platform supports multimaster operation, the peripheral
* performs arbitration automatically when detecting collisions and
* completes the transfer or returns I2C_ERROR_ARBITRATION_LOST
* when it loses arbitration.
*
* Additional time for arbitration or clock stretching should by count
* by setting appropriate timeout value.
*
* When no transmision timeout is set by the user, the default timeout value is
* used. It counts one additional byte for addressing stage:
* default_timeout = (length + 1) * byte_timeout.
*
* @return
* zero or nonzero - Number of written bytes
* negative - I2C_ERROR_XXX status
*/
int32_t i2c_write(i2c_t *obj, uint16_t address, const uint8_t *data,
uint32_t length, bool stop);
/** Blocking reading data
*
* This function receives data when the peripheral is configured as Master
* from the selected slave and when configured as Slave from the Master.
*
* This function is blocking; it returns when the transfer is complete or a
* timeout event is triggered. The number of bytes received is returned by
* the function after the operation is completed. Receive operation cannot be
* canceled or aborted.
*
* When in master mode, the operation consists of:
* - Addressing the slave as a Master receiver.
* - Receiving data from the addressed slave.
* - Generating a STOP condition if the specified `stop` field is true.
*
* @param obj The I2C object
* @param address 7/10-bit address (last bit is 1)
* @param data The buffer for receiving
* @param length Number of bytes to read
* @param stop If true, stop is generated after the transfer is done
*
* @note If the current platform supports multimaster operation, the peripheral
* performs arbitration automatically when detecting collisions and
* completes the transfer or returns I2C_ERROR_ARBITRATION_LOST
* when it loses arbitration.
*
* Additional time for arbitration or clock stretching should by count
* by setting appropriate timeout value.
*
* When no transmision timeout is set by the user, the default timeout value is
* used. It counts one additional byte for addressing stage:
* default_timeout = (length + 1) * byte_timeout.
*
* @return
* zero or nonzero - Number of written bytes
* negative - I2C_ERROR_XXX status
*/
int32_t i2c_read(i2c_t *obj, uint16_t address, uint8_t *data, uint32_t length,
bool stop);
/** Get the pins that support I2C SDA
*
* Return a PinMap array of pins that support I2C SDA in
* master mode. The array is terminated with {NC, NC, 0}.
*
* @return PinMap array
*/
const PinMap *i2c_master_sda_pinmap(void);
/** Get the pins that support I2C SCL
*
* Return a PinMap array of pins that support I2C SCL in
* master mode. The array is terminated with {NC, NC, 0}.
*
* @return PinMap array
*/
const PinMap *i2c_master_scl_pinmap(void);
/** Get the pins that support I2C SDA
*
* Return a PinMap array of pins that support I2C SDA in
* slave mode. The array is terminated with {NC, NC, 0}.
*
* @return PinMap array
*/
const PinMap *i2c_slave_sda_pinmap(void);
/** Get the pins that support I2C SCL
*
* Return a PinMap array of pins that support I2C SCL in
* slave mode. The array is terminated with {NC, NC, 0}.
*
* @return PinMap array
*/
const PinMap *i2c_slave_scl_pinmap(void);
/**@}*/
#if DEVICE_I2CSLAVE
/**
* \defgroup SynchI2C Synchronous I2C Hardware Abstraction Layer for slave
* @{
*/
/** Slave status
*
* @note Default status is Idle.
*/
typedef enum {
Idle = 0, // Slave has not been addressed.
ReadAddressed = 1, // Master has requested a read from this slave.
WriteGeneral = 2, // Master is writing to all slaves.
WriteAddressed = 3 // Master is writing to this slave.
} i2c_slave_status_t;
/** Check to see if the I2C slave has been addressed.
*
* @param obj The I2C object
* @return The status - i2c_slave_status_t indicating what state the peripheral
* is configured in.
*/
i2c_slave_status_t i2c_slave_status(i2c_t *obj);
/** Configure I2C address.
*
* @note This function does nothing when configured in master mode.
*
* @param obj The I2C object
* @param address The address to be set - 7 bit or 10 bit
*/
void i2c_slave_address(i2c_t *obj, uint16_t address);
#endif
/**@}*/
#if DEVICE_I2C_ASYNCH
/**
* \defgroup hal_AsynchI2C Asynchronous I2C Hardware Abstraction Layer
* @{
*/
/** Start I2C asynchronous transfer
*
* @param obj The I2C object
* @param tx The transmit buffer
* @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 - 7 bit or 10 bit
* @param stop If true, stop is generated after the transfer is done
* @param handler The I2C IRQ handler to be set
* @param ctx The context pointer
* @return true if the transfer was successfully scheduled, false otherwise
*/
bool i2c_transfer_async(i2c_t *obj, const uint8_t *tx, uint32_t tx_length,
uint8_t *rx, uint32_t rx_length, uint16_t address,
bool stop, i2c_async_handler_f handler, void *ctx);
/** Abort asynchronous transfer
*
* This function does not perform any check - that should happen in upper
* layers.
*
* @param obj The I2C object
*/
void i2c_abort_async(i2c_t *obj);
#endif
/**@}*/
#ifdef __cplusplus
}
#endif
#endif
#endif
/** @}*/