mirror of https://github.com/ARMmbed/mbed-os.git
656 lines
18 KiB
C
656 lines
18 KiB
C
/* mbed Microcontroller Library
|
|
* Copyright (c) 2018 GigaDevice Semiconductor Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "mbed_assert.h"
|
|
#include "i2c_api.h"
|
|
|
|
#if DEVICE_I2C
|
|
|
|
#include "cmsis.h"
|
|
#include "pinmap.h"
|
|
#include "PeripheralPins.h"
|
|
|
|
#if DEVICE_I2C_ASYNCH
|
|
#define I2C_S(obj) (struct i2c_s *) (&((obj)->i2c))
|
|
#else
|
|
#define I2C_S(obj) (struct i2c_s *) (obj)
|
|
#endif
|
|
|
|
#define BUSY_TIMEOUT ((SystemCoreClock / obj_s->freq) * 2 * 10)
|
|
#define FLAG_TIMEOUT (0x1000U)
|
|
|
|
/** Reset I2C peripheral by hardware method. Most of the implementation enable RCU reset.
|
|
*
|
|
* @param obj The I2C object
|
|
*/
|
|
static void i2c_hw_reset(i2c_t *obj)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
switch (obj_s->i2c) {
|
|
case I2C_0:
|
|
rcu_periph_reset_enable(RCU_I2C0RST);
|
|
rcu_periph_reset_disable(RCU_I2C0RST);
|
|
break;
|
|
|
|
case I2C_1:
|
|
rcu_periph_reset_enable(RCU_I2C1RST);
|
|
rcu_periph_reset_disable(RCU_I2C1RST);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Initialize the I2C peripheral. It sets the default parameters for I2C
|
|
* peripheral, and configures its specifieds pins.
|
|
*
|
|
* @param obj The I2C object
|
|
* @param sda The sda pin
|
|
* @param scl The scl pin
|
|
*/
|
|
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
/* find the I2C by pins */
|
|
uint32_t i2c_sda = pinmap_peripheral(sda, PinMap_I2C_SDA);
|
|
uint32_t i2c_scl = pinmap_peripheral(scl, PinMap_I2C_SCL);
|
|
|
|
obj_s->sda = sda;
|
|
obj_s->scl = scl;
|
|
obj_s->i2c = (I2CName)pinmap_merge(i2c_sda, i2c_scl);
|
|
MBED_ASSERT(obj_s->i2c != (I2CName)NC);
|
|
|
|
switch (obj_s->i2c) {
|
|
case I2C_0:
|
|
/* enable I2C0 clock and configure the pins of I2C0 */
|
|
obj_s->index = 0;
|
|
rcu_periph_clock_enable(RCU_I2C0);
|
|
|
|
break;
|
|
|
|
case I2C_1:
|
|
/* enable I2C1 clock and configure the pins of I2C1 */
|
|
obj_s->index = 1;
|
|
rcu_periph_clock_enable(RCU_I2C1);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* configure the pins of I2C */
|
|
pinmap_pinout(sda, PinMap_I2C_SDA);
|
|
pinmap_pinout(scl, PinMap_I2C_SCL);
|
|
|
|
/* 100 KHz as the default I2C frequence */
|
|
i2c_frequency(obj, 100000);
|
|
|
|
obj_s->state = (operation_state_enum)I2C_STATE_NONE;
|
|
obj_s->previous_state_mode = I2C_STATE_NONE;
|
|
obj_s->global_trans_option = I2C_FIRST_AND_LAST_FRAME;
|
|
|
|
#if DEVICE_I2CSLAVE
|
|
/* I2C master by default */
|
|
obj_s->slave = 0;
|
|
#endif
|
|
}
|
|
|
|
/** Configure the I2C frequency
|
|
*
|
|
* @param obj The I2C object
|
|
* @param hz Frequency in Hz
|
|
*/
|
|
void i2c_frequency(i2c_t *obj, int hz)
|
|
{
|
|
int timeout;
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
/* wait until I2C_FLAG_I2CBSY flag is reset */
|
|
timeout = BUSY_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_I2CBSY)) && (--timeout != 0));
|
|
|
|
/* reset to clear pending flags */
|
|
i2c_hw_reset(obj);
|
|
|
|
/* disable I2C peripheral */
|
|
i2c_disable(obj_s->i2c);
|
|
|
|
/* configure I2C frequence */
|
|
i2c_clock_config(obj_s->i2c, hz, I2C_DTCY_2);
|
|
|
|
/* configure I2C address mode and slave address */
|
|
i2c_mode_addr_config(obj_s->i2c, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0);
|
|
|
|
/* enable I2C peripheral */
|
|
i2c_enable(obj_s->i2c);
|
|
}
|
|
|
|
/** Reset I2C peripheral. TODO: The action here. Most of the implementation sends stop()
|
|
*
|
|
* @param obj The I2C object
|
|
*/
|
|
void i2c_reset(i2c_t *obj)
|
|
{
|
|
i2c_stop(obj);
|
|
}
|
|
|
|
/** Send START command
|
|
*
|
|
* @param obj The I2C object
|
|
*/
|
|
int i2c_start(i2c_t *obj)
|
|
{
|
|
int timeout;
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
/* clear I2C_FLAG_AERR Flag */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_AERR);
|
|
|
|
/* wait until I2C_FLAG_I2CBSY flag is reset */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_I2CBSY)) == SET) {
|
|
if ((timeout--) == 0) {
|
|
return (int)GD_BUSY;
|
|
}
|
|
}
|
|
|
|
/* ensure the i2c has been stopped */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((I2C_CTL0(obj_s->i2c) & I2C_CTL0_STOP) == I2C_CTL0_STOP) {
|
|
if ((timeout--) == 0) {
|
|
return (int)GD_ERROR;
|
|
}
|
|
}
|
|
|
|
/* generate a START condition */
|
|
i2c_start_on_bus(obj_s->i2c);
|
|
|
|
/* ensure the i2c has been started successfully */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_SBSEND)) == RESET) {
|
|
if ((timeout--) == 0) {
|
|
return (int)GD_ERROR;
|
|
}
|
|
}
|
|
|
|
return (int)GD_OK;
|
|
}
|
|
|
|
/** Send STOP command
|
|
*
|
|
* @param obj The I2C object
|
|
*/
|
|
int i2c_stop(i2c_t *obj)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
/* generate a STOP condition */
|
|
i2c_stop_on_bus(obj_s->i2c);
|
|
|
|
/* wait for STOP bit reset */
|
|
while ((I2C_CTL0(obj_s->i2c) & I2C_CTL0_STOP));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Read one byte
|
|
*
|
|
* @param obj The I2C object
|
|
* @param last Acknoledge
|
|
* @return The read byte
|
|
*/
|
|
int i2c_byte_read(i2c_t *obj, int last)
|
|
{
|
|
int timeout;
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
if (last) {
|
|
/* disable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
} else {
|
|
/* enable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
}
|
|
|
|
/* wait until the byte is received */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_RBNE)) == RESET) {
|
|
if ((timeout--) == 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return (int)I2C_DATA(obj_s->i2c);
|
|
}
|
|
|
|
/** Write one byte
|
|
*
|
|
* @param obj The I2C object
|
|
* @param data Byte to be written
|
|
* @return 0 if NAK was received, 1 if ACK was received, 2 for timeout.
|
|
*/
|
|
int i2c_byte_write(i2c_t *obj, int data)
|
|
{
|
|
int timeout;
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
I2C_DATA(obj_s->i2c) = (uint8_t)data;
|
|
|
|
/* wait until the byte is transmitted */
|
|
timeout = FLAG_TIMEOUT;
|
|
while (((i2c_flag_get(obj_s->i2c, I2C_FLAG_TBE)) == RESET) &&
|
|
((i2c_flag_get(obj_s->i2c, I2C_FLAG_BTC)) == RESET)) {
|
|
if ((timeout--) == 0) {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/** Blocking reading data
|
|
*
|
|
* @param obj The I2C object
|
|
* @param address 7-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
|
|
* @return Number of read bytes
|
|
*/
|
|
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
uint32_t count = 0U;
|
|
int timeout = 0;
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME ||
|
|
obj_s->global_trans_option == I2C_LAST_FRAME) {
|
|
if (stop) {
|
|
obj_s->global_trans_option = I2C_FIRST_AND_LAST_FRAME;
|
|
} else {
|
|
obj_s->global_trans_option = I2C_FIRST_FRAME;
|
|
}
|
|
} else if (obj_s->global_trans_option == I2C_FIRST_FRAME ||
|
|
obj_s->global_trans_option == I2C_NEXT_FRAME) {
|
|
if (stop) {
|
|
obj_s->global_trans_option = I2C_LAST_FRAME;
|
|
} else {
|
|
obj_s->global_trans_option = I2C_NEXT_FRAME;
|
|
}
|
|
}
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME || obj_s->global_trans_option == I2C_FIRST_FRAME) {
|
|
/* wait until I2C_FLAG_I2CBSY flag is reset */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_I2CBSY)) == SET) {
|
|
if ((timeout--) == 0) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_BUS_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME || obj_s->global_trans_option == I2C_FIRST_FRAME ||
|
|
obj_s->previous_state_mode != I2C_STATE_MASTER_BUSY_RX) {
|
|
/* generate a START condition */
|
|
i2c_start_on_bus(obj_s->i2c);
|
|
|
|
/* ensure the i2c has been started successfully */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_SBSEND)) == RESET) {
|
|
if ((timeout--) == 0) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_BUS_BUSY;
|
|
}
|
|
}
|
|
|
|
/* send slave address */
|
|
i2c_master_addressing(obj_s->i2c, address, I2C_RECEIVER);
|
|
|
|
if (1 == length) {
|
|
/* disable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
/* send a stop condition to I2C bus*/
|
|
} else if (2 == length) {
|
|
/* send a NACK for the next data byte which will be received into the shift register */
|
|
i2c_ackpos_config(obj_s->i2c, I2C_ACKPOS_NEXT);
|
|
/* disable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
} else {
|
|
/* enable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
}
|
|
|
|
/* wait until I2C_FLAG_ADDSEND flag is set */
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_ADDSEND)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_NO_SLAVE;
|
|
}
|
|
}
|
|
|
|
/* clear ADDSEND */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_ADDSEND);
|
|
}
|
|
|
|
obj_s->state = (operation_state_enum)I2C_STATE_MASTER_BUSY_RX;
|
|
|
|
for (count = 0; count < length; count++) {
|
|
if (length > 2 && count == length - 3) {
|
|
while (RESET == i2c_flag_get(obj_s->i2c, I2C_FLAG_BTC));
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
} else if (2 == length && count == 0) {
|
|
while (RESET == i2c_flag_get(obj_s->i2c, I2C_FLAG_BTC));
|
|
}
|
|
|
|
while (RESET == i2c_flag_get(obj_s->i2c, I2C_FLAG_RBNE));
|
|
data[count] = i2c_data_receive(obj_s->i2c);
|
|
}
|
|
|
|
obj_s->previous_state_mode = obj_s->state;
|
|
|
|
/* if not sequential read, then send stop */
|
|
if (stop) {
|
|
i2c_stop(obj);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/** Blocking sending data
|
|
*
|
|
* @param obj The I2C object
|
|
* @param address 7-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
|
|
* @return
|
|
* zero or non-zero - Number of written bytes
|
|
* negative - I2C_ERROR_XXX status
|
|
*/
|
|
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
gd_status_enum status = GD_OK;
|
|
uint32_t count = 0;
|
|
int timeout = 0;
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME ||
|
|
obj_s->global_trans_option == I2C_LAST_FRAME) {
|
|
if (stop) {
|
|
obj_s->global_trans_option = I2C_FIRST_AND_LAST_FRAME;
|
|
} else {
|
|
obj_s->global_trans_option = I2C_FIRST_FRAME;
|
|
}
|
|
} else if (obj_s->global_trans_option == I2C_FIRST_FRAME ||
|
|
obj_s->global_trans_option == I2C_NEXT_FRAME) {
|
|
if (stop) {
|
|
obj_s->global_trans_option = I2C_LAST_FRAME;
|
|
} else {
|
|
obj_s->global_trans_option = I2C_NEXT_FRAME;
|
|
}
|
|
}
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME || obj_s->global_trans_option == I2C_FIRST_FRAME) {
|
|
/* wait until I2C_FLAG_I2CBSY flag is reset */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_I2CBSY)) == SET) {
|
|
if ((timeout--) == 0) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_BUS_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj_s->global_trans_option == I2C_FIRST_AND_LAST_FRAME || obj_s->global_trans_option == I2C_FIRST_FRAME ||
|
|
obj_s->previous_state_mode != I2C_STATE_MASTER_BUSY_TX) {
|
|
/* generate a START condition */
|
|
i2c_start_on_bus(obj_s->i2c);
|
|
|
|
/* ensure the i2c has been started successfully */
|
|
timeout = FLAG_TIMEOUT;
|
|
while ((i2c_flag_get(obj_s->i2c, I2C_FLAG_SBSEND)) == RESET) {
|
|
if ((timeout--) == 0) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_BUS_BUSY;
|
|
}
|
|
}
|
|
|
|
/* send slave address */
|
|
i2c_master_addressing(obj_s->i2c, address, I2C_TRANSMITTER);
|
|
|
|
/* wait until I2C_FLAG_ADDSEND flag is set */
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_ADDSEND)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
i2c_stop(obj);
|
|
return I2C_ERROR_NO_SLAVE;
|
|
}
|
|
}
|
|
|
|
/* clear ADDSEND */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_ADDSEND);
|
|
}
|
|
|
|
obj_s->state = (operation_state_enum)I2C_STATE_MASTER_BUSY_TX;
|
|
|
|
for (count = 0; count < length; count++) {
|
|
status = (gd_status_enum)i2c_byte_write(obj, data[count]);
|
|
if (status != 1) {
|
|
i2c_stop(obj);
|
|
return count;
|
|
}
|
|
}
|
|
|
|
obj_s->previous_state_mode = obj_s->state;
|
|
|
|
/* if not sequential write, then send stop */
|
|
if (stop) {
|
|
i2c_stop(obj);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
#if DEVICE_I2CSLAVE
|
|
|
|
/** Configure I2C address.
|
|
* @param obj The I2C object
|
|
* @param idx Currently not used
|
|
* @param address The address to be set
|
|
* @param mask Currently not used
|
|
*/
|
|
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
/* disable I2C peripheral */
|
|
i2c_disable(obj_s->i2c);
|
|
/* I2C clock configure */
|
|
i2c_clock_config(obj_s->i2c, 100000, I2C_DTCY_2);
|
|
/* I2C address configure */
|
|
i2c_mode_addr_config(obj_s->i2c, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, address);
|
|
/* enable I2C0 */
|
|
i2c_enable(obj_s->i2c);
|
|
/* enable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
}
|
|
|
|
/** Configure I2C as slave or master.
|
|
* @param obj The I2C object
|
|
* @param enable_slave Enable i2c hardware so you can receive events with ::i2c_slave_receive
|
|
* @return non-zero if a value is available
|
|
*/
|
|
void i2c_slave_mode(i2c_t *obj, int enable_slave)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
|
|
if (enable_slave) {
|
|
obj_s->slave = 1;
|
|
} else {
|
|
obj_s->slave = 0;
|
|
}
|
|
}
|
|
|
|
/* the same as the definition in I2CSlave.h class I2CSlave */
|
|
#define NoData 0 /* the slave has not been addressed */
|
|
#define ReadAddressed 1 /* the master has requested a read from this slave (slave as transmitter) */
|
|
#define WriteGeneral 2 /* the master is writing to all slave */
|
|
#define WriteAddressed 3 /* the master is writing to this slave (slave as receiver) */
|
|
|
|
/** Check to see if the I2C slave has been addressed.
|
|
* @param obj The I2C object
|
|
* @return The status - 1 - read addresses, 2 - write to all slaves,
|
|
* 3 write addressed, 0 - the slave has not been addressed
|
|
*/
|
|
int i2c_slave_receive(i2c_t *obj)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
int ret = NoData;
|
|
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
|
|
if (i2c_flag_get(obj_s->i2c, I2C_FLAG_ADDSEND)) {
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
if (i2c_flag_get(obj_s->i2c, I2C_FLAG_RXGC)) {
|
|
ret = WriteGeneral;
|
|
}
|
|
|
|
if (i2c_flag_get(obj_s->i2c, I2C_FLAG_TRS)) {
|
|
ret = ReadAddressed;
|
|
} else {
|
|
ret = WriteAddressed;
|
|
}
|
|
}
|
|
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/** Configure I2C as slave or master.
|
|
* @param obj The I2C object
|
|
* @param data The buffer for receiving
|
|
* @param length Number of bytes to read
|
|
* @return non-zero if a value is available
|
|
*/
|
|
int i2c_slave_read(i2c_t *obj, char *data, int length)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
int count = 0;
|
|
int timeout = 0;
|
|
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
|
|
/* wait until ADDSEND bit is set */
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_ADDSEND)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
return -1;
|
|
}
|
|
}
|
|
/* clear ADDSEND bit */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_ADDSEND);
|
|
|
|
while (0 < length) {
|
|
/* wait until the RBNE bit is set */
|
|
timeout = 0;
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_RBNE)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
return -1;
|
|
}
|
|
}
|
|
*data = i2c_data_receive(obj_s->i2c);
|
|
data++;
|
|
length--;
|
|
count++;
|
|
}
|
|
/* wait until the STPDET bit is set */
|
|
timeout = 0;
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_STPDET)) {
|
|
timeout++;
|
|
if (timeout > 100) {
|
|
return count;
|
|
}
|
|
}
|
|
/* clear the STPDET bit */
|
|
i2c_enable(obj_s->i2c);
|
|
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
|
|
return count;
|
|
}
|
|
|
|
/** Configure I2C as slave or master.
|
|
* @param obj The I2C object
|
|
* @param data The buffer for sending
|
|
* @param length Number of bytes to write
|
|
* @return non-zero if a value is available
|
|
*/
|
|
int i2c_slave_write(i2c_t *obj, const char *data, int length)
|
|
{
|
|
struct i2c_s *obj_s = I2C_S(obj);
|
|
int count = 0;
|
|
int timeout = 0;
|
|
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_ENABLE);
|
|
/* wait until ADDSEND bit is set */
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_ADDSEND)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
return -1;
|
|
}
|
|
}
|
|
/* clear ADDSEND bit */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_ADDSEND);
|
|
while (length > 0) {
|
|
/* wait until the TBE bit is set */
|
|
timeout = 0;
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_TBE)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
return -1;
|
|
}
|
|
}
|
|
i2c_data_transmit(obj_s->i2c, *data);
|
|
data++;
|
|
length--;
|
|
count++;
|
|
}
|
|
/* the master doesn't acknowledge for the last byte */
|
|
timeout = 0;
|
|
while (!i2c_flag_get(obj_s->i2c, I2C_FLAG_AERR)) {
|
|
timeout++;
|
|
if (timeout > 100000) {
|
|
return -1;
|
|
}
|
|
}
|
|
/* clear the bit of AERR */
|
|
i2c_flag_clear(obj_s->i2c, I2C_FLAG_AERR);
|
|
/* disable acknowledge */
|
|
i2c_ack_config(obj_s->i2c, I2C_ACK_DISABLE);
|
|
|
|
return count;
|
|
}
|
|
#endif /* DEVICE_I2CSLAVE */
|
|
|
|
#endif /* DEVICE_I2C */
|