Merge pull request #12778 from GaborAbonyi/add_i2c_to_musca_b1

Add I2C api to Musca-B1 target
pull/12811/head
Martin Kojtal 2020-04-15 16:53:35 +02:00 committed by GitHub
commit 4c08455125
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 2480 additions and 0 deletions

View File

@ -30,6 +30,11 @@ typedef enum {
UART_1
} UARTName;
typedef enum {
I2C_0 = 0,
I2C_1
} I2CName;
#define STDIO_UART_TX UART1_TX
#define STDIO_UART_RX UART1_RX
#define STDIO_UART UART_1

View File

@ -86,6 +86,12 @@ typedef enum {
LED2 = PA3,
LED3 = PA4,
/* I2C pins */
I2C0_SDA = PA14, /* Alternate Function - 1 */
I2C0_SCL = PA15, /* Alternate Function - 1 */
I2C1_SDA = PA18,
I2C1_SCL = PA19,
/* Not connected */
NC = (int)0xFFFFFFFF
} PinName;

View File

@ -0,0 +1,647 @@
/*
* Copyright (c) 2018-2020 Arm Limited
*
* 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.
*/
/**
* \file i2c_ip6510_drv.h
* \brief Driver for IP6510 I2C controller.
*/
#ifndef __I2C_IP6510_DRV_H__
#define __I2C_IP6510_DRV_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief I2C (IP6510) device state types.
*/
enum i2c_ip6510_state_t {
I2C_IP6510_UNINITIALIZED = 0u,
I2C_IP6510_INITIALIZED = 1u,
};
/**
* \brief I2C (IP6510) bus state types.
*/
enum i2c_ip6510_bus_state_t {
I2C_IP6510_BUS_INACTIVE = 0u,
I2C_IP6510_BUS_ACTIVE = 1u,
};
/**
* \brief Allowed transfer direction options.
*/
enum i2c_ip6510_transf_dir_t {
I2C_IP6510_TRANSMITTER = 0u,
I2C_IP6510_RECEIVER = 1u,
};
/**
* \brief Supported I2C (IP6510) device mode options.
*/
enum i2c_ip6510_device_mode_t {
I2C_IP6510_SLAVE_MODE = 0u,
I2C_IP6510_MASTER_MODE = 1u,
};
/**
* \brief Supported I2C (IP6510) addressing options.
*/
enum i2c_ip6510_addr_mode_t {
I2C_IP6510_EXT_ADDR_MODE = 0u,
I2C_IP6510_NOR_ADDR_MODE = 1u,
};
/**
* \brief Supported I2C (IP6510) bus data rate options.
*/
enum i2c_ip6510_speed_t {
I2C_IP6510_SPEED_100KHZ = 0u,
I2C_IP6510_SPEED_400KHZ = 1u,
I2C_IP6510_SPEED_MAX_SUPPORTED = 2u,
};
#define I2C_IP6510_INTR_COMP_OFF (0u)
/*!< Transfer complete interrupt bit field offset */
#define I2C_IP6510_INTR_DATA_OFF (1u)
/*!< More data interrupt bit field offset */
#define I2C_IP6510_INTR_NACK_OFF (2u)
/*!< Transfer not ACKed interrupt bit field offset */
#define I2C_IP6510_INTR_TO_OFF (3u)
/*!< Transfer time out interrupt bit field offset */
#define I2C_IP6510_INTR_SLVRDY_OFF (4u)
/*!< Monitored slave ready interrupt bit field offset */
#define I2C_IP6510_INTR_RXOVF_OFF (5u)
/*!< Receive overflow interrupt bit field offset */
#define I2C_IP6510_INTR_TXOVF_OFF (6u)
/*!< FIFO transmit overflow interrupt bit field offset */
#define I2C_IP6510_INTR_RXUNF_OFF (7u)
/*!< FIFO receive underflow interrupt bit field offset */
#define I2C_IP6510_INTR_ARBLOST_OFF (9u)
/*!< Arbitration lost interrupt bit field offset */
#define I2C_IP6510_ALL_INTR_MASK (0x2FFu)
/**
* \brief I2C (IP6510) interrupt data structure
*/
enum i2c_ip6510_intr_t {
I2C_IP6510_INTR_COMP_MASK = (0x1u<<I2C_IP6510_INTR_COMP_OFF),
I2C_IP6510_INTR_DATA_MASK = (0x1u<<I2C_IP6510_INTR_DATA_OFF),
I2C_IP6510_INTR_NACK_MASK = (0x1u<<I2C_IP6510_INTR_NACK_OFF),
I2C_IP6510_INTR_TO_MASK = (0x1u<<I2C_IP6510_INTR_TO_OFF),
I2C_IP6510_INTR_SLVRDY_MASK = (0x1u<<I2C_IP6510_INTR_SLVRDY_OFF),
I2C_IP6510_INTR_RXOVF_MASK = (0x1u<<I2C_IP6510_INTR_RXOVF_OFF),
I2C_IP6510_INTR_TXOVF_MASK = (0x1u<<I2C_IP6510_INTR_TXOVF_OFF),
I2C_IP6510_INTR_RXUNF_MASK = (0x1u<<I2C_IP6510_INTR_RXUNF_OFF),
I2C_IP6510_INTR_ARBLOST_MASK = (0x1u<<I2C_IP6510_INTR_ARBLOST_OFF),
};
/**
* \brief I2C (IP6510) error enumeration types.
*/
enum i2c_ip6510_error_t {
I2C_IP6510_ERR_NONE = 0u,
I2C_IP6510_ERR_NOT_INIT = 1u,
I2C_IP6510_ERR_ALREADY_INIT = 2u,
I2C_IP6510_ERR_INVALID_ARG = 3u,
I2C_IP6510_ERR_NACK = 4u,
I2C_IP6510_ERR_RXOVF = 5u,
I2C_IP6510_ERR_TXOVF = 6u,
I2C_IP6510_ERR_RXUNF = 7u,
I2C_IP6510_ERR_TIMEOUT = 8u,
I2C_IP6510_ERR = 9u,
};
/**
* \brief I2C (IP6510) device configuration structure.
*/
struct i2c_ip6510_dev_cfg_t {
const uint32_t base;
/*!< I2C device base address */
const enum i2c_ip6510_speed_t default_bus_speed;
/*!< I2C bus data rate */
const enum i2c_ip6510_device_mode_t default_mode;
/*!< I2C device mode (Master/Slave) */
};
/**
* \brief I2C (IP6510) device data structure.
*/
struct i2c_ip6510_dev_data_t {
enum i2c_ip6510_state_t state; /*!< I2C device state */
enum i2c_ip6510_device_mode_t mode; /*!< I2C device mode (Master/Slave) */
enum i2c_ip6510_speed_t bus_speed; /*!< I2C bus operational data rate */
uint32_t sys_clk; /*!< System clock frequency */
};
/**
* \brief I2C (IP6510) device structure.
*/
struct i2c_ip6510_dev_t {
const struct i2c_ip6510_dev_cfg_t* const cfg;
/*!< I2C (IP6510) configuration structure */
struct i2c_ip6510_dev_data_t* const data;
/*!< I2C (IP6510) data structure */
};
/**
* \brief Initializes I2C (IP6510) device.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] sys_clk System clock frequency used by the device.
*
* It configures the interface as I2C Master with the default data rate
* and disables every I2C interrupts.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This API should be called before calling any of the below I2C APIs.
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_init(struct i2c_ip6510_dev_t* dev,
uint32_t sys_clk);
/**
* \brief Uninitializes I2C (IP6510) device and resets registers.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_uninit(struct i2c_ip6510_dev_t* dev);
/**
* \brief Enables Hold mode.
*
* Sets hold bit, so the master does not terminate automatically a read or
* write transfer.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_hold_enable(const struct i2c_ip6510_dev_t* dev);
/**
* \brief Disables Hold mode.
*
* Resets hold bit, so the master can terminate automatically a read or
* write transfer.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_hold_disable(const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the I2C (IP6510) device operational state.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the I2C (IP6510) device state \ref i2c_ip6510_state_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_state_t i2c_ip6510_get_state(
const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the I2C interface mode.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the I2C interface mode \ref i2c_ip6510_device_mode_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_device_mode_t i2c_ip6510_get_device_mode(
const struct i2c_ip6510_dev_t* dev);
/**
* \brief Sets the I2C data rate.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] speed New I2C data rate \ref i2c_ip6510_speed_t
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note The I2C device should be in valid state before calling this API.
* \ref i2c_ip6510_get_state should return I2C_IP6510_INITIALIZED.
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_set_speed(struct i2c_ip6510_dev_t* dev,
enum i2c_ip6510_speed_t speed);
/**
* \brief Returns the actual I2C data rate.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the actual I2C data rate \ref i2c_ip6510_speed_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_speed_t i2c_ip6510_get_speed(
const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the state of the I2C bus.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the I2C bus state \ref i2c_ip6510_bus_state_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_bus_state_t i2c_ip6510_get_bus_status(
const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the transfer size registers value.
*
* Transfer size registers value contains:
* Master transmitter mode: number of data bytes still not transmitted
* minus one.
* Master receiver mode: number of data bytes that are still expected
* to be received.
* Slave transmitter mode: number of bytes remaining in the FIFO after
* the master terminates the transfer.
* Slave receiver mode: number of valid data bytes in the FIFO.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the transfer size registers value.
*
* \note This function doesn't check if dev is NULL.
*/
int i2c_ip6510_get_transfer_size(const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the I2C interrupt status.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return The returned status value can be checked against values from
* the \ref i2c_ip6510_intr_t type.
*
* \note This function doesn't check if dev is NULL.
*/
uint32_t i2c_ip6510_get_irq_status(const struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the I2C interrupt mask status.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return The returned mask value can be checked against values from
* the \ref i2c_ip6510_intr_t type.
*
* \note This function doesn't check if dev is NULL.
*/
uint32_t i2c_ip6510_get_irq_mask(const struct i2c_ip6510_dev_t* dev);
/**
* \brief Clears the specified I2C interrupts.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] mask Bit mask for clearing interrupts. Values from
* \ref i2c_ip6510_intr_t could be used to create the mask.
*
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_clear_irq(struct i2c_ip6510_dev_t* dev, uint32_t mask);
/**
* \brief Enables the specified I2C interrupts.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] mask Bit mask for enabling interrupts. Values from
* \ref i2c_ip6510_intr_t could be used to create the mask.
*
* \note User is responsible to configure the interrupt vector and
* the interrupt controller.
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_enable_irq(struct i2c_ip6510_dev_t* dev, uint32_t mask);
/**
* \brief Disables I2C interrupts.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] mask Bit mask for disabling interrupts. Values from
* \ref i2c_ip6510_intr_t could be used to create the mask.
*
* \note User is responsible to configure the interrupt vector and
* the interrupt controller.
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_disable_irq(struct i2c_ip6510_dev_t* dev, uint32_t mask);
/**
* \brief Sets the time out interval.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] interval New time out interval in number of scl cycles minus one.
*
* \note The default time out interval is 0x20.
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_set_timeout(struct i2c_ip6510_dev_t* dev,
uint8_t interval);
/**
* \brief Sets the pause interval in slave monitor mode.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] interval New pause interval in number of scl cycles minus one.
*
* \note The default pause interval is 0 (continuous monitoring).
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_set_slave_monitor_pause_interval(struct i2c_ip6510_dev_t* dev,
uint8_t interval);
/**
* \brief Returns the pause interval of slave monitor mode.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the pause interval of slave monitor mode
* in number of scl cycles minus one.
*
* \note The default pause interval is 0 (continuous monitoring).
* \note This function doesn't check if dev is NULL.
*/
uint8_t i2c_ip6510_get_slave_monitor_pause_interval(
struct i2c_ip6510_dev_t* dev);
/**
* \brief Sets the length of the glitch filter.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] length New length of the glitch filter [0(min) ... 15(max)].
*
* \note If length of glitch filter is set to 0 then the glitch filter is
* bypassed. The default glitch filter length is 0.
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_set_glitch_filter_length(struct i2c_ip6510_dev_t* dev,
uint32_t length);
/**
* \brief Returns the length of the glitch filter.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Returns the actual length of the glitch filter.
*
* \note The default glitch filter length is 0.
* \note This function doesn't check if dev is NULL.
*/
uint8_t i2c_ip6510_get_glitch_filter_length(const struct i2c_ip6510_dev_t* dev);
/**
* \brief The I2C device monitors if addressed I2C slave is present on the bus.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr Address of the monitored I2C device (7 bits or 10 bits).
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
* Return value is I2C_IP6510_ERR_NONE if the addressed I2C slave is
* present on the bus.
*
* \note The I2C interface should be in Master mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_MASTER_MODE.
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_monitor_slave(struct i2c_ip6510_dev_t* dev,
uint16_t addr);
/**
* \brief Writes data to I2C device.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr I2C device address (7 bits or 10 bits).
* \param[in] tx_data Data buffer pointer to write.
* \param[in] stop If false, master does not generate STOP symbol.
* \param[in,out] tx_length Data buffer length (number of bytes to write).
* On return: stores the number of bytes written.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note The I2C interface should be in Master mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_MASTER_MODE.
* \note This function doesn't check if the pointers are NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_master_write(struct i2c_ip6510_dev_t* dev,
uint16_t addr,
const uint8_t* tx_data,
bool stop,
uint32_t* tx_length);
/**
* \brief Reads data from I2C device.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr I2C device address (7 bits or 10 bits).
* \param[in] rx_data Buffer pointer to store the read data.
* \param[in] stop If false, master does not generate STOP symbol.
* \param[in,out] rx_length Buffer length (number of bytes to read).
* On return: stores the number of read bytes.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note The I2C interface should be in Master mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_MASTER_MODE.
* \note This function doesn't check if the pointers are NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_master_read(struct i2c_ip6510_dev_t* dev,
uint16_t addr,
uint8_t* rx_data,
bool stop,
uint32_t* rx_length);
/**
* \brief Writes to and reads from I2C device (combined transfer).
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr I2C device address (7 bits or 10 bits).
* \param[in] tx_data Data buffer pointer to write.
* \param[in] rx_data Buffer pointer to store the read data.
* \param[in,out] tx_length Data buffer length (number of bytes to write).
* On return: stores the number of bytes written.
* \param[in,out] rx_length Buffer length (number of bytes to read).
* On return: stores the number of read bytes.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note The I2C interface should be in Master mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_MASTER_MODE.
* \note This function doesn't check if the pointers are NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_master_write_read(
struct i2c_ip6510_dev_t* dev,
uint16_t addr,
const uint8_t* tx_data,
uint8_t* rx_data,
uint32_t* tx_length,
uint32_t* rx_length);
/*
* \brief Writes one byte to I2C device
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr Address to write to, accepts both 7 and 10 bit
* addresses
* \param[in] tx_data Data pointer to write.
* \param[in] set_addr Set true for the first data byte. If true,
* overwrites address register, starting a new
* transaction. Set false for all following bytes in
* the same transaction.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_master_byte_write(
const struct i2c_ip6510_dev_t* dev,
uint16_t addr,
const uint8_t* tx_data,
bool set_addr);
/*
* \brief Reads one byte from I2C device
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr Address to read from, accepts both 7 and 10 bit
* addresses
* \param[in] last_byte If true ends transmission on the next byte received
* \param[in] set_addr Set true for the first data byte. If true,
* overwrites address register, starting a new
* transaction. Set false for all following bytes in
* the same transaction.
* \param[out] rx_data Data pointer to read to.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_master_byte_read(
const struct i2c_ip6510_dev_t* dev,
uint16_t addr,
bool last_byte,
bool set_addr,
uint8_t* rx_data);
/**
* \brief Sets the I2C interface to be in slave mode with the given address.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] addr Unique identifying address of the I2C interface.
*
* It sets the I2C interface to be in slave mode with an address which
* identifies the interface on the I2C bus and sets the appropriate I2C
* data rate for the slave mode.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_set_slave_mode(
struct i2c_ip6510_dev_t* dev,
uint16_t addr);
/**
* \brief Sets the I2C interface to be in master mode
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* It only sets the I2C interface to be in master mode thus it will not answer
* to further transfer requests.
*
* \note This function doesn't check if dev is NULL.
*/
void i2c_ip6510_set_master_mode(struct i2c_ip6510_dev_t* dev);
/**
* \brief Returns the direction of the transmission received from the Master.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
*
* \return Received direction of the transmission \ref i2c_ip6510_transf_dir_t
*
* \note This function doesn't check if dev is NULL.
*/
enum i2c_ip6510_transf_dir_t i2c_ip6510_get_slave_tranf_dir(
struct i2c_ip6510_dev_t* dev);
/**
* \brief Sends data over the I2C bus to the Master.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] tx_data Data buffer pointer to write.
* \param[in,out] tx_length Data buffer length (number of bytes to write).
* On return: stores the number of bytes written.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This API should be called only if transmission request has been
* received from the Master.
* \note The I2C interface should be in Slave mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_SLAVE_MODE.
* \note This function doesn't check if the pointers are NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_slave_write(struct i2c_ip6510_dev_t* dev,
const uint8_t* tx_data,
uint32_t* tx_length);
/**
* \brief Receives data from I2C Master.
*
* \param[in] dev I2C (IP6510) device structure \ref i2c_ip6510_dev_t
* \param[in] rx_data Buffer pointer to store the read data.
* \param[in,out] rx_length Buffer length (number of bytes to read), a system
* known parameter or must be communicated in advance.
* On return: stores the number of bytes read.
*
* \return Returns error code as specified in \ref i2c_ip6510_error_t
*
* \note This API should be called only if transmission request has been
* received from the Master.
* \note The I2C interface should be in Slave mode before calling this API,
* for better performance the function doesn't check this,
* \ref i2c_ip6510_get_device_mode should return I2C_IP6510_SLAVE_MODE.
* \note This function doesn't check if the pointers are NULL.
*/
enum i2c_ip6510_error_t i2c_ip6510_slave_read(struct i2c_ip6510_dev_t* dev,
uint8_t* rx_data,
uint32_t* rx_length);
#ifdef __cplusplus
}
#endif
#endif /* __I2C_IP6510_DRV_H__ */

View File

@ -0,0 +1,425 @@
/* mbed Microcontroller Library
* Copyright (c) 2017-2020 Arm Limited
*
* 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.
*/
#include "i2c_api.h"
#include "mbed_error.h"
#include "i2c_ip6510_drv.h"
#include "objects.h"
#include "pinmap.h"
#include "PeripheralNames.h"
/* Return error codes for the i2c_byte_write api */
#define BYTE_TRANSFER_ERR_NACK (0)
#define BYTE_TRANSFER_ERR_NONE (1)
#define BYTE_TRANSFER_ERR_TIMEOUT (2)
/* Macros for frequency configuration */
#define I2C_SPEED_100KHZ (100000)
#define I2C_SPEED_400KHZ (400000)
#if DEVICE_I2CSLAVE
/* Return values for slave addressing specified in mbed I2C slave driver */
#define I2C_SLAVE_READ_ADDRESS (1)
#define I2C_SLAVE_WRITE_ALL_ADDRESS (2)
#define I2C_SLAVE_WRITE_ADDRESS (3)
#define I2C_SLAVE_NOT_ADDRESSED (0)
#endif
extern const PinMap PinMap_I2C_SDA[];
extern const PinMap PinMap_I2C_SCL[];
/* Extend the return values defined in i2c_api.h */
enum {
I2C_ERROR_NONE = 0,
/* I2C_ERROR_NO_SLAVE = -1,
* I2C_ERROR_BUS_BUSY = -2, */
I2C_ERROR_GENERAL = -3
};
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
enum i2c_ip6510_error_t ret;
/* Determine the I2C to use */
I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
I2CName i2c = (I2CName)pinmap_merge(i2c_sda, i2c_scl);
if (i2c == (I2CName)NC) {
error("I2C pinout mapping failed, invalid pins.");
return;
}
/* Initialize the parent object */
switch (i2c) {
#ifdef I2C0_IP6510_DEV
case I2C_0:
obj->dev = &I2C0_IP6510_DEV;
break;
#endif
#ifdef I2C1_IP6510_DEV
case I2C_1:
obj->dev = &I2C1_IP6510_DEV;
break;
#endif
default:
error("Failed to initialize I2C, bad reference.");
return;
}
obj->last_address = 0U;
obj->sda = sda;
obj->scl = scl;
obj->byte_state = BYTE_TRANSFER_STATE_NONE;
/* If already init, uninit */
if (i2c_ip6510_get_state(obj->dev) == I2C_IP6510_INITIALIZED) {
i2c_ip6510_uninit(obj->dev);
}
/* Set the GPIO pins */
pinmap_pinout(sda, PinMap_I2C_SDA);
pinmap_pinout(scl, PinMap_I2C_SCL);
/* Initialize peripheral */
ret = i2c_ip6510_init(obj->dev, SystemCoreClock);
i2c_ip6510_set_speed(obj->dev, I2C_IP6510_SPEED_100KHZ);
i2c_ip6510_set_timeout(obj->dev, 0xFFU);
if (ret != I2C_IP6510_ERR_NONE) {
error("Failed to initialize I2C, error occured in native driver.");
return;
}
}
void i2c_frequency(i2c_t *obj, int hz)
{
/* The peripheral only supports 100k and 400k clock in master mode */
switch (hz) {
case I2C_SPEED_100KHZ:
i2c_ip6510_set_speed(obj->dev, I2C_IP6510_SPEED_100KHZ);
break;
case I2C_SPEED_400KHZ:
i2c_ip6510_set_speed(obj->dev, I2C_IP6510_SPEED_400KHZ);
break;
default:
error("Invalid I2C frequency.");
return;
}
}
void i2c_reset(i2c_t *obj)
{
i2c_ip6510_uninit(obj->dev);
i2c_init(obj, obj->sda, obj->scl);
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
enum i2c_ip6510_error_t ret;
/* Shifted 8 bit address is used in upper layer */
address = ((uint32_t)address)>>1;
ret = i2c_ip6510_master_read(obj->dev, (uint16_t)address, (uint8_t*)data,
stop, (uint32_t*)(&length));
if (ret != I2C_IP6510_ERR_NONE) {
return I2C_ERROR_GENERAL;
}
return length;
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
enum i2c_ip6510_error_t ret;
/* Shifted 8 bit address is used in upper layer */
address = ((uint32_t)address)>>1;
/* Parameter checking */
if (data == NULL || length == 0) {
ret = i2c_ip6510_monitor_slave(obj->dev, (uint16_t)address);
if (ret != I2C_IP6510_ERR_NONE) {
return I2C_ERROR_NO_SLAVE;
}
return length;
}
ret = i2c_ip6510_master_write(obj->dev, (uint16_t)address, (uint8_t*)data,
stop, (uint32_t*)(&length));
if (ret != I2C_IP6510_ERR_NONE) {
return I2C_ERROR_GENERAL;
}
return length;
}
int i2c_start(i2c_t *obj)
{
/** \note The peripheral does not support building up the transaction by
* instructions. The functionality is achieved with a software
* state machine.
* The i2c transaction is started later, when the address and the
* first data byte is written.
*/
obj->byte_state = BYTE_TRANSFER_STATE_START;
obj->last_address = 0U;
i2c_ip6510_hold_enable(obj->dev);
return I2C_ERROR_NONE;
}
int i2c_stop(i2c_t *obj)
{
/* Both the master and slave api calls this function */
if (i2c_ip6510_get_device_mode(obj->dev) == I2C_IP6510_MASTER_MODE) {
/** \note The peripheral does not support building up the transaction by
* instructions. The functionality is achieved with a software
* state machine.
* STOP condition is generated on hold disable.
*/
i2c_ip6510_hold_disable(obj->dev);
/* If there was only one byte written (the slave address) before calling
* stop, the state machine is in ADDRESS state. Writing just the address
* is equal to a slave monitoring
*/
if (obj->byte_state == BYTE_TRANSFER_STATE_ADDRESS) {
/* Return value is not needed because signalling to the caller is
* not defined
*/
(void)i2c_ip6510_monitor_slave(obj->dev, obj->last_address);
}
obj->byte_state = BYTE_TRANSFER_STATE_NONE;
obj->last_address = 0U;
} else {
/* In slave mode the the I2C controller only writes and reads data from
* the I2C bus. The function just clears the interrupts for
* further adressing detection.
*/
i2c_ip6510_clear_irq(obj->dev, I2C_IP6510_ALL_INTR_MASK);
}
return I2C_ERROR_NONE;
}
int i2c_byte_read(i2c_t *obj, int last)
{
uint8_t read_byte = 0U;
uint32_t slave_read_cntr = 1U;
/* Both the master and slave api calls this function */
if (i2c_ip6510_get_device_mode(obj->dev) == I2C_IP6510_MASTER_MODE) {
/** \note The peripheral does not support building up the transaction by
* instructions. The functionality is achieved with a software
* state machine.
*/
switch (obj->byte_state) {
case BYTE_TRANSFER_STATE_ADDRESS:
obj->byte_state = BYTE_TRANSFER_STATE_DATA;
i2c_ip6510_master_byte_read(
obj->dev, obj->last_address, last, true, &read_byte);
return read_byte;
case BYTE_TRANSFER_STATE_DATA:
i2c_ip6510_master_byte_read(
obj->dev, obj->last_address, last, false, &read_byte);
return read_byte;
case BYTE_TRANSFER_STATE_NONE:
case BYTE_TRANSFER_STATE_START:
default:
/* Reading is invalid in these states */
return I2C_ERROR_GENERAL;
}
} else {
/* In slave mode the driver only writes and reads data from the
* I2C controller, no need to track states.
*/
i2c_ip6510_slave_read(
obj->dev, &read_byte, &slave_read_cntr);
return read_byte;
}
}
int i2c_byte_write(i2c_t *obj, int data)
{
enum i2c_ip6510_error_t ret = I2C_IP6510_ERR_NONE;
uint32_t slave_write_cntr = 1U;
/* Both the master and slave api calls this function */
if (i2c_ip6510_get_device_mode(obj->dev) == I2C_IP6510_MASTER_MODE) {
/** \note The peripheral does not support building up the transaction by
* instructions. The functionality is achieved with a software
* state machine.
*/
switch (obj->byte_state) {
case BYTE_TRANSFER_STATE_NONE:
/* Writing is invalid before start symbol
* BYTE_TRANSFER_ERR_TIMEOUT is the only error code mbed defines,
* this is the only way to signal an error
*/
return BYTE_TRANSFER_ERR_TIMEOUT;
case BYTE_TRANSFER_STATE_START:
obj->byte_state = BYTE_TRANSFER_STATE_ADDRESS;
obj->last_address = ((uint32_t)data)>>1;
return BYTE_TRANSFER_ERR_NONE;
case BYTE_TRANSFER_STATE_ADDRESS:
obj->byte_state = BYTE_TRANSFER_STATE_DATA;
ret = i2c_ip6510_master_byte_write(
obj->dev, obj->last_address, (uint8_t*)&data, true);
break;
case BYTE_TRANSFER_STATE_DATA:
ret = i2c_ip6510_master_byte_write(
obj->dev, obj->last_address, (uint8_t*)&data, false);
break;
default:
return BYTE_TRANSFER_ERR_TIMEOUT;
}
} else {
/* In slave mode the driver only writes and reads data from the
* I2C controller, no need to track states.
*/
ret = i2c_ip6510_slave_write(
obj->dev, (uint8_t*)&data, &slave_write_cntr);
}
if (ret != I2C_IP6510_ERR_NONE) {
/* No need to reset the state machine. The host might try to resend
* the data byte. Also the start and stop functions reset the states.
*/
if (ret == I2C_IP6510_ERR_NACK) {
return BYTE_TRANSFER_ERR_NACK;
}
return BYTE_TRANSFER_ERR_TIMEOUT;
}
return BYTE_TRANSFER_ERR_NONE;
}
#if DEVICE_I2CSLAVE
void i2c_slave_mode(i2c_t *obj, int enable_slave)
{
if (!enable_slave) {
/* Check if master mode is already set */
if (i2c_ip6510_get_device_mode(obj->dev) != I2C_IP6510_MASTER_MODE) {
/* Set Master Mode */
i2c_ip6510_set_master_mode(obj->dev);
}
} else {
/* Check if slave mode is already set */
if (i2c_ip6510_get_device_mode(obj->dev) != I2C_IP6510_SLAVE_MODE) {
/* Set Slave Mode */
i2c_ip6510_set_slave_mode(obj->dev, obj->last_address);
}
}
}
int i2c_slave_receive(i2c_t *obj)
{
uint32_t irq_status = i2c_ip6510_get_irq_status(obj->dev);
enum i2c_ip6510_transf_dir_t dir = i2c_ip6510_get_slave_tranf_dir(obj->dev);
uint32_t transfer_size = i2c_ip6510_get_transfer_size(obj->dev);
if (irq_status & I2C_IP6510_INTR_DATA_MASK) {
if (dir == I2C_IP6510_TRANSMITTER) {
/* Slave is adressed for writing */
return I2C_SLAVE_WRITE_ADDRESS;
}
if ((dir == I2C_IP6510_RECEIVER) && (transfer_size != 0U)) {
/* Slave is adressed for reading */
return I2C_SLAVE_READ_ADDRESS;
}
}
if ((irq_status & I2C_IP6510_INTR_COMP_MASK)
&& (dir == I2C_IP6510_RECEIVER) && (transfer_size != 0U)) {
/* An I2C transfer is complete with less then FIFO_SIZE-2 bytes */
return I2C_SLAVE_READ_ADDRESS;
}
return I2C_SLAVE_NOT_ADDRESSED;
}
int i2c_slave_read(i2c_t *obj, char *data, int length)
{
enum i2c_ip6510_error_t ret;
ret = i2c_ip6510_slave_read(
obj->dev, (uint8_t*)data, (uint32_t*)&length);
i2c_ip6510_clear_irq(obj->dev, I2C_IP6510_ALL_INTR_MASK);
if (ret != I2C_IP6510_ERR_NONE) {
return 0;
}
return length;
}
int i2c_slave_write(i2c_t *obj, const char *data, int length)
{
enum i2c_ip6510_error_t ret;
ret = i2c_ip6510_slave_write(
obj->dev, (uint8_t*)data, (uint32_t*)&length);
i2c_ip6510_clear_irq(obj->dev, I2C_IP6510_ALL_INTR_MASK);
if (ret != I2C_IP6510_ERR_NONE) {
return 0;
}
return length;
}
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
{
/* Shifted 8 bit address is used in upper layer */
i2c_ip6510_set_slave_mode(obj->dev, ((uint16_t)address)>>1);
obj->last_address = address>>1;
}
const PinMap *i2c_slave_sda_pinmap()
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_slave_scl_pinmap()
{
return PinMap_I2C_SCL;
}
#endif
const PinMap *i2c_master_sda_pinmap()
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_master_scl_pinmap()
{
return PinMap_I2C_SCL;
}

View File

@ -32,6 +32,16 @@
#include "rtx_lib.h"
const PinMap PinMap_I2C_SDA[] = {
{I2C0_SDA, I2C_0, ALTERNATE_FUNC_1},
{I2C1_SDA, I2C_1, PRIMARY_FUNC}
};
const PinMap PinMap_I2C_SCL[] = {
{I2C0_SCL, I2C_0, ALTERNATE_FUNC_1},
{I2C1_SCL, I2C_1, PRIMARY_FUNC}
};
/**
* \brief Translates between different pin mode enums
*

View File

@ -108,6 +108,12 @@
#define GP_TIMER_FREQ_HZ 32768UL /* System Ref Clock */
#define GP_TIMER_BIT_WIDTH 32U
/* I2C IP6510 */
#define I2C0_IP6510_NS
#define I2C0_IP6510_DEV I2C0_IP6510_DEV_NS
#define I2C1_IP6510_NS
#define I2C1_IP6510_DEV I2C1_IP6510_DEV_NS
/**
* mbed usec high-resolution ticker configuration
*/

View File

@ -54,6 +54,23 @@ struct trng_s {
};
#endif // DEVICE_TRNG
#if DEVICE_I2C || DEVICE_I2CSLAVE
enum byte_transfer_states {
BYTE_TRANSFER_STATE_NONE = 0,
BYTE_TRANSFER_STATE_START,
BYTE_TRANSFER_STATE_ADDRESS,
BYTE_TRANSFER_STATE_DATA,
};
struct i2c_s {
struct i2c_ip6510_dev_t *dev;
uint16_t last_address;
PinName sda;
PinName scl;
enum byte_transfer_states byte_state;
};
#endif
#ifdef __cplusplus
}
#endif

View File

@ -8452,6 +8452,8 @@
],
"device_has_add": [
"INTERRUPTIN",
"I2C",
"I2CSLAVE",
"LPTICKER",
"SERIAL",
"SLEEP",