[CMSIS][LPC1768] I2C bus locked up

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

[CMSIS][LPC1768] I2C bus locked up

Jump to solution
3,285 Views
yakoinfinity
Contributor II

Hi! I'm new on 32bit microcontrollers programming -and on the forums too!.

I have experiencing some problems on the I2C communication between LPC1768 and PCA9535. I'm using LPCxpresso IDE V8.2.2, Windows 10, CMSIS Drivers and CoOS.

When I try to communicate with the PCA9535 (slave device), sometimes my MCU sends the adress (in this case, 0x23), the slave tries to ACK, but my microcontroller doesn't send SCL line down. 

Other times, It sends the adress and some data (for example, Address, command and one byte) and then SCL line stuck high. 

I have no clue of what would be happenning...

 

 

/***********************************************************************//**
* @file lpc17xx_i2c.c
* @brief Contains all functions support for I2C firmware library on LPC17xx
* @version 2.0
* @date 21. May. 2010
* @author NXP MCU SW Application Team
**************************************************************************

/* Peripheral group ----------------------------------------------------------- */
/** @addtogroup I2C
* @{
*/

/* Includes ------------------------------------------------------------------- */
#include <drivers/i2c.h>
#include <drivers/clkpwr.h>
#include <drivers/pinsel.h>
#include <drivers/PCA9535x.h>
#include <bsp.h>
#include <drivers/io.h>

/* If this source file built with example, the LPC17xx FW library configuration
* file in each example directory ("lpc17xx_libcfg.h") must be included,
* otherwise the default FW library configuration file must be included instead
*/
/* #ifdef __BUILD_WITH_EXAMPLE__
#include "lpc17xx_libcfg.h"
#else
#include "lpc17xx_libcfg_default.h"
#endif __BUILD_WITH_EXAMPLE__ */


#if BSP_I2C_EN

FlagStatus completed_I2C = RESET;

/* Private Types -------------------------------------------------------------- */
/** @defgroup I2C_Private_Types I2C Private Types
* @{
*/

/**
* @brief I2C device configuration structure type
*/

typedef struct
{
uint32_t txrx_setup; /* Transmission setup */
int32_t dir; /* Current direction phase, 0 - write, 1 - read */
} I2C_CFG_T;

/**
* @}
*/

/* Private Variables ---------------------------------------------------------- */
/**
* @brief II2C driver data for I2C0, I2C1 and I2C2
*/
static I2C_CFG_T i2cdat[3];

static uint32_t I2C_MasterComplete[3];
static uint32_t I2C_SlaveComplete[3];

static uint32_t I2C_MonitorBufferIndex;

/* Private Functions ---------------------------------------------------------- */

/* Get I2C number */
static int32_t I2C_getNum(I2C_TypeDef *I2Cx);

/* Generate a start condition on I2C bus (in master mode only) */
static uint32_t I2C_Start (I2C_TypeDef *I2Cx);

/* Generate a stop condition on I2C bus (in master mode only) */
static void I2C_Stop (I2C_TypeDef *I2Cx);

/* I2C send byte subroutine */
static uint32_t I2C_SendByte (I2C_TypeDef *I2Cx, uint8_t databyte);

/* I2C get byte subroutine */
static uint32_t I2C_GetByte (I2C_TypeDef *I2Cx, uint8_t *retdat, uint8_t ack);

/* I2C set clock (hz) */
static void I2C_SetClock (I2C_TypeDef *I2Cx, uint32_t target_clock);

/*--------------------------------------------------------------------------------*/
/********************************************************************//**
* @brief Convert from I2C peripheral to number
* @param[in] I2Cx: I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return I2C number, could be: 0..2
*********************************************************************/
static int32_t I2C_getNum(I2C_TypeDef *I2Cx){
if (I2Cx == I2C0) {
return (0);
} else if (I2Cx == I2C1) {
return (1);
} else if (I2Cx == I2C2) {
return (2);
}
return (-1);
}

/********************************************************************//**
* @brief Generate a start condition on I2C bus (in master mode only)
* @param[in] I2Cx: I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return value of I2C status register after generate a start condition
*********************************************************************/
static uint32_t I2C_Start (I2C_TypeDef *I2Cx)
{

I2Cx->I2CONSET = I2C_I2CONSET_STA;
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;

// Wait for complete
while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI));

I2Cx->I2CONCLR = I2C_I2CONCLR_STAC;
return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK);
}

/********************************************************************//**
* @brief Generate a stop condition on I2C bus (in master mode only)
* @param[in] I2Cx: I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return None
*********************************************************************/
static void I2C_Stop (I2C_TypeDef *I2Cx)
{

/* Make sure start bit is not active */
if (I2Cx->I2CONSET & I2C_I2CONSET_STA)
{
I2Cx->I2CONCLR = I2C_I2CONCLR_STAC;
}
I2Cx->I2CONSET = I2C_I2CONSET_STO | I2C_I2CONSET_AA;
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
}

/********************************************************************//**
* @brief Send a byte
* @param[in] I2Cx: I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @param[in] databyte: number of byte
* @return value of I2C status register after sending
*********************************************************************/
static uint32_t I2C_SendByte (I2C_TypeDef *I2Cx, uint8_t databyte)
{
/* Make sure start bit is not active */
if (I2Cx->I2CONSET & I2C_I2CONSET_STA)
{
I2Cx->I2CONCLR = I2C_I2CONCLR_STAC;
}
I2Cx->I2DAT = databyte & I2C_I2DAT_BITMASK;
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;

while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI));
return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK);
}

/********************************************************************//**
* @brief Get a byte
* @param[in] I2Cx: I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @param[out] retdat pointer to return data
* @param[in] ack assert acknowledge or not, should be: TRUE/FALSE
* @return value of I2C status register after sending
*********************************************************************/
static uint32_t I2C_GetByte (I2C_TypeDef *I2Cx, uint8_t *retdat, uint8_t ack)
{
if (ack == TRUE)
{
I2Cx->I2CONSET = I2C_I2CONSET_AA;
}
else
{
I2Cx->I2CONCLR = I2C_I2CONCLR_AAC;
}
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;

while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI));
*retdat = (uint8_t) (I2Cx->I2DAT & I2C_I2DAT_BITMASK);
return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK);
}

/*********************************************************************//**
* @brief Setup clock rate for I2C peripheral
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @param[in] target_clock : clock of SSP (Hz)
* @return None
***********************************************************************/
static void I2C_SetClock (I2C_TypeDef *I2Cx, uint32_t target_clock)
{
uint32_t temp;

//CHECK_PARAM(PARAM_I2Cx(I2Cx));

// Get PCLK of I2C controller
if (I2Cx == I2C0)
{
temp = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_I2C0) / target_clock;
}
else if (I2Cx == I2C1)
{
temp = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_I2C1) / target_clock;
}
else if (I2Cx == I2C2)
{
temp = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_I2C1) / target_clock;
}

/* Set the I2C clock value to register */
I2Cx->I2SCLH = (uint32_t)(temp / 2);
I2Cx->I2SCLL = (uint32_t)(temp - I2Cx->I2SCLH);
}
/* End of Private Functions --------------------------------------------------- */


/* Public Functions ----------------------------------------------------------- */
/** @addtogroup I2C_Public_Functions
* @{
*/

/********************************************************************//**
* @brief Initializes the I2Cx peripheral with specified parameter.
* @param[in] I2Cx I2C peripheral selected, should be
* - I2C0
* - I2C1
* - I2C2
* @param[in] clockrate Target clock rate value to initialized I2C
* peripheral (Hz)
* @return None
*********************************************************************/
void I2C_Init(I2C_TypeDef *I2Cx, uint32_t clockrate)
{
//CHECK_PARAM(PARAM_I2Cx(I2Cx));

if (I2Cx==I2C0)
{
/* Set up clock and power for I2C0 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C0, ENABLE);
/* As default, peripheral clock for I2C0 module
* is set to FCCLK / 2 */
CLKPWR_SetPCLKDiv(CLKPWR_PCLKSEL_I2C0, CLKPWR_PCLKSEL_CCLK_DIV_2);

//Select SDA0 and SCL0 pins
PINSEL_SetPinFunc(PINSEL_PORT_0, PINSEL_PIN_27, PINSEL_FUNC_1);
PINSEL_SetPinFunc(PINSEL_PORT_0, PINSEL_PIN_28, PINSEL_FUNC_1);
PINSEL_SetI2C0Pins(PINSEL_I2C_Normal_Mode, ENABLE);

/*clear SI and STA flags*/
I2Cx->I2CONCLR |= I2C_I2CONCLR_SIC;
I2Cx->I2CONCLR |= I2C_I2CONCLR_STAC;


}
else if (I2Cx==I2C1)
{
/* Set up clock and power for I2C1 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C1, ENABLE);
/* As default, peripheral clock for I2C1 module
* is set to FCCLK / 2 */
CLKPWR_SetPCLKDiv(CLKPWR_PCLKSEL_I2C1, CLKPWR_PCLKSEL_CCLK_DIV_2);
}
else if (I2Cx==I2C2)
{
/* Set up clock and power for I2C2 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C2, ENABLE);
/* As default, peripheral clock for I2C2 module
* is set to FCCLK / 2 */
CLKPWR_SetPCLKDiv(CLKPWR_PCLKSEL_I2C2, CLKPWR_PCLKSEL_CCLK_DIV_2);
}
else {
// Up-Support this device
return;
}

/* Set clock rate */
I2C_SetClock(I2Cx, clockrate);
/* Set I2C operation to default */
I2Cx->I2CONCLR = (I2C_I2CONCLR_AAC | I2C_I2CONCLR_STAC | I2C_I2CONCLR_I2ENC);
PINCON->I2CPADCFG &=~(1<<0)|(1<<1)|(1<<2)|(1<<3);
}

/*********************************************************************//**
* @brief De-initializes the I2C peripheral registers to their
* default reset values.
* @param[in] I2Cx I2C peripheral selected, should be
* - I2C0
* - I2C1
* - I2C2
* @return None
**********************************************************************/
void I2C_DeInit(I2C_TypeDef* I2Cx)
{
//CHECK_PARAM(PARAM_I2Cx(I2Cx));

/* Disable I2C control */
I2Cx->I2CONCLR = I2C_I2CONCLR_I2ENC;

if (I2Cx==I2C0)
{
/* Disable power for I2C0 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C0, DISABLE);
}
else if (I2Cx==I2C1)
{
/* Disable power for I2C1 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C1, DISABLE);
}
else if (I2Cx==I2C2)
{
/* Disable power for I2C2 module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCI2C2, DISABLE);
}
}

/*********************************************************************//**
* @brief Enable or disable I2C peripheral's operation
* @param[in] I2Cx I2C peripheral selected, should be
* - I2C0
* - I2C1
* - I2C2
* @param[in] NewState New State of I2Cx peripheral's operation
* @return none
**********************************************************************/
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
//CHECK_PARAM(PARAM_FUNCTIONALSTATE(NewState));
//CHECK_PARAM(PARAM_I2Cx(I2Cx));

if (NewState == ENABLE)
{
I2Cx->I2CONSET = I2C_I2CONSET_I2EN;
}
else
{
I2Cx->I2CONCLR = I2C_I2CONCLR_I2ENC;
}

}

/*********************************************************************//**
* @brief Enable/Disable interrupt for I2C peripheral
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @param[in] NewState New State of I2C peripheral interrupt in NVIC core
* should be:
* - ENABLE: enable interrupt for this I2C peripheral
* - DISABLE: disable interrupt for this I2C peripheral
* @return None
**********************************************************************/
void I2C_IntCmd (I2C_TypeDef *I2Cx, uint8_t NewState)
{
if (NewState)
{
if(I2Cx == I2C0)
{
NVIC_EnableIRQ(I2C0_IRQn);
}
else if (I2Cx == I2C1)
{
NVIC_EnableIRQ(I2C1_IRQn);
}
else if (I2Cx == I2C2)
{
NVIC_EnableIRQ(I2C2_IRQn);
}
}
else
{
if(I2Cx == I2C0)
{
NVIC_DisableIRQ(I2C0_IRQn);
}
else if (I2Cx == I2C1)
{
NVIC_DisableIRQ(I2C1_IRQn);
}
else if (I2Cx == I2C2)
{
NVIC_DisableIRQ(I2C2_IRQn);
}
}
return;
}


/*********************************************************************//**
* @brief General Master Interrupt handler for I2C peripheral
* @param[in] I2Cx I2C peripheral selected, should be:
* - LPC_I2C
* - I2C1
* - I2C2
* @return None
**********************************************************************/
void I2C_MasterHandler (I2C_TypeDef *I2Cx)
{
int32_t tmp;
uint8_t returnCode;
I2C_M_SETUP_Type *txrx_setup;

tmp = I2C_getNum(I2Cx);
txrx_setup = (I2C_M_SETUP_Type *) i2cdat[tmp].txrx_setup;

returnCode = (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK);
// Save current status
txrx_setup->status = returnCode;
// there's no relevant information
if (returnCode == I2C_I2STAT_NO_INF){
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
return;
}

/* ----------------------------- TRANSMIT PHASE --------------------------*/
if (i2cdat[tmp].dir == 0){
switch (returnCode)
{
/* A start/repeat start condition has been transmitted -------------------*/
case I2C_I2STAT_M_TX_START:
case I2C_I2STAT_M_TX_RESTART:
I2Cx->I2CONCLR = I2C_I2CONCLR_STAC;
/*
* If there's any transmit data, then start to
* send SLA+W right now, otherwise check whether if there's
* any receive data for next state.
*/
if ((txrx_setup->tx_data != NULL) && (txrx_setup->tx_length != 0)){
I2Cx->I2DAT = (txrx_setup->sl_addr7bit << 1);
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
} else {
goto next_stage;
}
break;

/* SLA+W has been transmitted, ACK has been received ----------------------*/
case I2C_I2STAT_M_TX_SLAW_ACK:
/* Data has been transmitted, ACK has been received */
case I2C_I2STAT_M_TX_DAT_ACK:
/* Send more data */
if ((txrx_setup->tx_count < txrx_setup->tx_length) \
&& (txrx_setup->tx_data != NULL)){
I2Cx->I2DAT = *(uint8_t *)(txrx_setup->tx_data + txrx_setup->tx_count);
txrx_setup->tx_count++;
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
}
// no more data, switch to next stage
else {
next_stage:
// change direction
i2cdat[tmp].dir = 1;
// Check if any data to receive
if ((txrx_setup->rx_length != 0) && (txrx_setup->rx_data != NULL)){
// check whether if we need to issue an repeat start
if ((txrx_setup->tx_length != 0) && (txrx_setup->tx_data != NULL)){
// Send out an repeat start command
I2Cx->I2CONSET = I2C_I2CONSET_STA;
I2Cx->I2CONCLR = I2C_I2CONCLR_AAC | I2C_I2CONCLR_SIC;
}
// Don't need issue an repeat start, just goto send SLA+R
else {
goto send_slar;
}
}
// no more data send, the go to end stage now
else {
// success, goto end stage
txrx_setup->status |= I2C_SETUP_STATUS_DONE;
goto end_stage;
}
}
break;

/* SLA+W has been transmitted, NACK has been received ----------------------*/
case I2C_I2STAT_M_TX_SLAW_NACK:
/* Data has been transmitted, NACK has been received -----------------------*/
case I2C_I2STAT_M_TX_DAT_NACK:
// update status
txrx_setup->status |= I2C_SETUP_STATUS_NOACKF;
goto retry;
/* Arbitration lost in SLA+R/W or Data bytes -------------------------------*/
case I2C_I2STAT_M_TX_ARB_LOST:
// update status
txrx_setup->status |= I2C_SETUP_STATUS_ARBF;
default:
goto retry;
}
}

/* ----------------------------- RECEIVE PHASE --------------------------*/
else if (i2cdat[tmp].dir == 1){
switch (returnCode){
/* A start/repeat start condition has been transmitted ---------------------*/
case I2C_I2STAT_M_RX_START:
case I2C_I2STAT_M_RX_RESTART:
I2Cx->I2CONCLR = I2C_I2CONCLR_STAC;
/*
* If there's any receive data, then start to
* send SLA+R right now, otherwise check whether if there's
* any receive data for end of state.
*/
if ((txrx_setup->rx_data != NULL) && (txrx_setup->rx_length != 0)){
send_slar:
I2Cx->I2DAT = (txrx_setup->sl_addr7bit << 1) | 0x01;
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
} else {
// Success, goto end stage
txrx_setup->status |= I2C_SETUP_STATUS_DONE;
goto end_stage;
}
break;

/* SLA+R has been transmitted, ACK has been received -----------------*/
case I2C_I2STAT_M_RX_SLAR_ACK:
if (txrx_setup->rx_count < (txrx_setup->rx_length - 1)) {
/*Data will be received, ACK will be return*/
I2Cx->I2CONSET = I2C_I2CONSET_AA;
}
else {
/*Last data will be received, NACK will be return*/
I2Cx->I2CONCLR = I2C_I2CONSET_AA;
}
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
break;

/* Data has been received, ACK has been returned ----------------------*/
case I2C_I2STAT_M_RX_DAT_ACK:
// Note save data and increase counter first, then check later
/* Save data */
if ((txrx_setup->rx_data != NULL) && (txrx_setup->rx_count < txrx_setup->rx_length)){
*(uint8_t *)(txrx_setup->rx_data + txrx_setup->rx_count) = (I2Cx->I2DAT & I2C_I2DAT_BITMASK);
txrx_setup->rx_count++;
}
if (txrx_setup->rx_count < (txrx_setup->rx_length - 1)) {
/*Data will be received, ACK will be return*/
I2Cx->I2CONSET = I2C_I2CONSET_AA;
}
else {
/*Last data will be received, NACK will be return*/
I2Cx->I2CONCLR = I2C_I2CONSET_AA;
}

I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
break;

/* Data has been received, NACK has been return -------------------------*/
case I2C_I2STAT_M_RX_DAT_NACK:
/* Save the last data */
if ((txrx_setup->rx_data != NULL) && (txrx_setup->rx_count < txrx_setup->rx_length)){
*(uint8_t *)(txrx_setup->rx_data + txrx_setup->rx_count) = (I2Cx->I2DAT & I2C_I2DAT_BITMASK);
txrx_setup->rx_count++;
}
// success, go to end stage
txrx_setup->status |= I2C_SETUP_STATUS_DONE;
goto end_stage;

/* SLA+R has been transmitted, NACK has been received ------------------*/
case I2C_I2STAT_M_RX_SLAR_NACK:
// update status
txrx_setup->status |= I2C_SETUP_STATUS_NOACKF;
goto retry;

/* Arbitration lost ----------------------------------------------------*/
case I2C_I2STAT_M_RX_ARB_LOST:
// update status
txrx_setup->status |= I2C_SETUP_STATUS_ARBF;
default:
retry:
// check if retransmission is available
if (txrx_setup->retransmissions_count < txrx_setup->retransmissions_max){
// Clear tx count
txrx_setup->tx_count = 0;
I2Cx->I2CONSET = I2C_I2CONSET_STA;
I2Cx->I2CONCLR = I2C_I2CONCLR_AAC | I2C_I2CONCLR_SIC;
txrx_setup->retransmissions_count++;
}
// End of stage
else {
end_stage:
// Disable interrupt
I2C_IntCmd(I2Cx, 0);
// Send stop
I2C_Stop(I2Cx);

I2C_MasterComplete[tmp] = TRUE;
}
break;
}
}
}

 

/*********************************************************************//**
* @brief Transmit and Receive data in master mode
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @param[in] TransferCfg Pointer to a I2C_M_SETUP_Type structure that
* contains specified information about the
* configuration for master transfer.
* @param[in] Opt a I2C_TRANSFER_OPT_Type type that selected for
* interrupt or polling mode.
* @return SUCCESS or ERROR
*
* Note:
* - In case of using I2C to transmit data only, either transmit length set to 0
* or transmit data pointer set to NULL.
* - In case of using I2C to receive data only, either receive length set to 0
* or receive data pointer set to NULL.
* - In case of using I2C to transmit followed by receive data, transmit length,
* transmit data pointer, receive length and receive data pointer should be set
* corresponding.
**********************************************************************/
Status I2C_MasterTransferData(I2C_TypeDef *I2Cx, I2C_M_SETUP_Type *TransferCfg, \
I2C_TRANSFER_OPT_Type Opt)
{
uint8_t *txdat;
uint8_t *rxdat;
uint32_t CodeStatus;
uint8_t tmp;

// reset all default state
txdat = (uint8_t *) TransferCfg->tx_data;
rxdat = (uint8_t *) TransferCfg->rx_data;
// Reset I2C setup value to default state
TransferCfg->tx_count = 0;
TransferCfg->rx_count = 0;
TransferCfg->status = 0;

 

if (Opt == I2C_TRANSFER_POLLING){
CodeStatus = 0;
/* First Start condition -------------------------------------------------------------- */
TransferCfg->retransmissions_count = 0;
retry:
// reset all default state
txdat = (uint8_t *) TransferCfg->tx_data;
rxdat = (uint8_t *) TransferCfg->rx_data;
// Reset I2C setup value to default state
TransferCfg->tx_count = 0;
TransferCfg->rx_count = 0;

CodeStatus = 0;

// Start command
CodeStatus = I2C_Start(I2Cx);
if ((CodeStatus != I2C_I2STAT_M_TX_START) \
&& (CodeStatus != I2C_I2STAT_M_TX_RESTART)){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// save status
TransferCfg->status = CodeStatus;
goto error;
} else {

goto retry;
}
}

/* In case of sending data first --------------------------------------------------- */
if ((TransferCfg->tx_length != 0) && (TransferCfg->tx_data != NULL)){

/* Send slave address + WR direction bit = 0 ----------------------------------- */
CodeStatus = I2C_SendByte(I2Cx, (TransferCfg->sl_addr7bit << 1));
if (CodeStatus != I2C_I2STAT_M_TX_SLAW_ACK){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// save status
TransferCfg->status = CodeStatus | I2C_SETUP_STATUS_NOACKF;
goto error;
} else {
goto retry;
}
}

/* Send a number of data bytes ---------------------------------------- */
while (TransferCfg->tx_count < TransferCfg->tx_length)
{
CodeStatus = I2C_SendByte(I2Cx, *txdat);
if (CodeStatus != I2C_I2STAT_M_TX_DAT_ACK){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// save status
TransferCfg->status = CodeStatus | I2C_SETUP_STATUS_NOACKF;
goto error;
} else {
goto retry;
}
}

txdat++;
TransferCfg->tx_count++;
}
}

/* Second Start condition (Repeat Start) ------------------------------------------- */
if ((TransferCfg->tx_length != 0) && (TransferCfg->tx_data != NULL) \
&& (TransferCfg->rx_length != 0) && (TransferCfg->rx_data != NULL)){

CodeStatus = I2C_Start(I2Cx);
if ((CodeStatus != I2C_I2STAT_M_RX_START) \
&& (CodeStatus != I2C_I2STAT_M_RX_RESTART)){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// Update status
TransferCfg->status = CodeStatus;
goto error;
} else {
goto retry;
}
}
}

/* Then, start reading after sending data -------------------------------------- */
if ((TransferCfg->rx_length != 0) && (TransferCfg->rx_data != NULL)){
/* Send slave address + RD direction bit = 1 ----------------------------------- */

CodeStatus = I2C_SendByte(I2Cx, ((TransferCfg->sl_addr7bit << 1) | 0x01));
if (CodeStatus != I2C_I2STAT_M_RX_SLAR_ACK){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// update status
TransferCfg->status = CodeStatus | I2C_SETUP_STATUS_NOACKF;
goto error;
} else {
goto retry;
}
}

/* Receive a number of data bytes ------------------------------------------------- */
while (TransferCfg->rx_count < TransferCfg->rx_length){

/*
* Note that: if data length is only one, the master should not
* issue an ACK signal on bus after reading to avoid of next data frame
* on slave side
*/
if (TransferCfg->rx_count < (TransferCfg->rx_length - 1)){
// Issue an ACK signal for next data frame
CodeStatus = I2C_GetByte(I2Cx, &tmp, 1);
if (CodeStatus != I2C_I2STAT_M_RX_DAT_ACK){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// update status
TransferCfg->status = CodeStatus;
goto error;
} else {
goto retry;
}
}
} else {
// Do not issue an ACK signal
CodeStatus = I2C_GetByte(I2Cx, &tmp, 0);
if (CodeStatus != I2C_I2STAT_M_RX_DAT_NACK){
TransferCfg->retransmissions_count++;
if (TransferCfg->retransmissions_count > TransferCfg->retransmissions_max){
// update status
TransferCfg->status = CodeStatus;
goto error;
} else {
goto retry;
}
}
}
*rxdat++ = tmp;
TransferCfg->rx_count++;
}
}

/* Send STOP condition ------------------------------------------------- */
I2C_Stop(I2Cx);
return SUCCESS;

error:
// Send stop condition
I2C_Stop(I2Cx);
return ERROR;
}

else if (Opt == I2C_TRANSFER_INTERRUPT){
// Setup tx_rx data, callback and interrupt handler
tmp = I2C_getNum(I2Cx);
i2cdat[tmp].txrx_setup = (uint32_t) TransferCfg;
// Set direction phase, write first
i2cdat[tmp].dir = 0;

/* First Start condition -------------------------------------------------------------- */
I2Cx->I2CONCLR = I2C_I2CONCLR_SIC;
I2Cx->I2CONSET = I2C_I2CONSET_STA;
I2C_IntCmd(I2Cx, 1);

return (SUCCESS);
}

return ERROR;

}

 

 



/*********************************************************************//**
* @brief Get status of Master Transfer
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return Master transfer status, could be:
* - TRUE master transfer completed
* - FALSE master transfer have not completed yet
**********************************************************************/
uint32_t I2C_MasterTransferComplete(I2C_TypeDef *I2Cx)
{
uint32_t retval, tmp;
tmp = I2C_getNum(I2Cx);
retval = I2C_MasterComplete[tmp];
I2C_MasterComplete[tmp] = FALSE;
return retval;
}

 

 


/* Interrupt handler subroutines for I2Cx*/
void I2C0_IRQHandler(void)
{
I2C_MasterHandler(I2C0);
if (I2C_MasterTransferComplete(I2C0)){
completed_I2C = SET;
}
}

void I2C1_IRQHandler(void)
{

I2C_MasterHandler(I2C1);

}

void I2C2_IRQHandler(void)
{

I2C_MasterHandler(I2C2);

}
/**
* @}
*/

#endif /* _I2C */

/**
* @}
*/

/* --------------------------------- End Of File ------------------------------ */

 

And this is my driver

#include <drivers/PCA9535x.h>


void Init_PCA9535 (PCA9535_SETUP_TYPE* regs, uint8_t adress, uint32_t clockrate)
{
I2C_M_SETUP_Type TransferCfg;
uint8_t obuff[3] = {CONFIGPORT0, regs->config_port0, regs->config_port1};
TransferCfg.sl_addr7bit = BASEADRESS | adress;
TransferCfg.tx_data = obuff;
TransferCfg.tx_length = sizeof(obuff);
TransferCfg.rx_data = NULL;
TransferCfg.rx_length = 0;
TransferCfg.retransmissions_max = 5;

I2C_Init(I2C0, clockrate); //Initialize I2C0, clockrate
I2C_Cmd(I2C0,ENABLE); //Enable I2C
#if I2C_TRANSFER_OPT
I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_INTERRUPT); //I2C0, interrupt mode.
while (completed_I2C == RESET);
#else
I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_POLLING); //I2C0, polling mode.
#endif
}
Status read_port(PCA9535_SETUP_TYPE* regs, uint8_t address)
{
I2C_M_SETUP_Type TransferCfg;
uint8_t ibuff [2] = {regs->input_port0, regs->input_port1};
uint8_t obuff = READPORT0; //READPORT0 (0x00) command
Status status;

TransferCfg.sl_addr7bit = BASEADRESS | address;
TransferCfg.tx_data = &obuff;
TransferCfg.tx_length = sizeof(obuff); //Transmit only the READPORT0 command
TransferCfg.rx_data = ibuff;
TransferCfg.rx_count = sizeof(ibuff); //receive the two output ports of PCA9535x.
TransferCfg.retransmissions_max = 5;

#if I2C_TRANSFER_OPT
status = I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_INTERRUPT); //I2C0, interrupt mode.
while (completed_I2C == RESET);
#else
status= I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_POLLING); //I2C0, polling mode.
#endif
return status;
}
Status set_port(PCA9535_SETUP_TYPE* regs, uint8_t adress)
{
I2C_M_SETUP_Type TransferCfg;
uint8_t obuff[3] = {WRITEPORT0, regs->output_port0, regs->output_port1 };
int status;

TransferCfg.sl_addr7bit = BASEADRESS | adress;
TransferCfg.tx_data = obuff;
TransferCfg.tx_length = sizeof(obuff);
TransferCfg.rx_data = NULL;//Do not receive anything
TransferCfg.rx_length = 0;
TransferCfg.retransmissions_max = 5;

#if I2C_TRANSFER_OPT
status = I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_INTERRUPT); //I2C0, interrupt mode.
while (completed_I2C == RESET);
#else
status= I2C_MasterTransferData(I2C0, &TransferCfg, I2C_TRANSFER_POLLING); //I2C0, polling mode.
#endif
return status;

}

 

Thank you in advance!

Regards

Exequiel

Original Attachment has been moved to: pca9535x.c.zip

Original Attachment has been moved to: pca9535x.h.zip

Original Attachment has been moved to: i2c.c.zip

Original Attachment has been moved to: i2c.h.zip

Labels (2)
1 Solution
1,824 Views
yakoinfinity
Contributor II

Thank you for your reply! I solved the problem already -but not stopped having troubles over here hahaha-

Reading more carefully LPC1768 Datasheet, found that if SDA line is pulled low, I can recover sending manually clock pulses over SCL line. You can see it on the page 472, chapter 19.9.7.4  "I2C-bus obstructed by a LOW level on SCL or SDA".

This is my recovery function (called on timeout of start, send or get byte functions)

/*********************************************************************//**
* @brief Release bus from SDA low level condition
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return None
**********************************************************************/
void I2C_BusRelease(I2C_TypeDef *I2Cx){
port_pin_t I2C_SCL;
I2C_SCL.port = GPIO0;
I2C_SCL.pin = 28;

int i = 0;
U64 clock_count;

I2C_DeInit(I2Cx);

//Init P0.28 as GPIO and force a dummy clock
gpio_init(&I2C_SCL,GPIO_OUTPUT);
clock_count = os_tick_count();

while(1){
if(os_tick_count() - clock_count >= 100){
i2c_toggle_clk(&I2C_SCL);
i++;
clock_count = os_tick_count();
if(i == 16){
break;
}
}
}
//re-init I2C0
I2C_Init(I2Cx, I2C_ADDRESS);
I2C_Cmd(I2Cx, ENABLE);
}

void i2c_toggle_clk(port_pin_t *pin){
if(gpio_get(pin) == TRUE){
gpio_clear(pin);
} else {
gpio_set(pin);
}
}

On the other hand, I have another problems related with the communication. Specifically with the the first five or four packages. Should I post a new question? Thank you! 

View solution in original post

4 Replies
1,824 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Exequiel Beker,

   Because I don't have PCA9535 device on my side, then please post some your I2C wave when the problem happens, it will be useful to my analysis.

  Especially the I2C wave which contains the device tries to ACK, but MCU does't send  SCL line down wave.

Waiting for your reply!


Have a great day,
Kerry

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

1,825 Views
yakoinfinity
Contributor II

Thank you for your reply! I solved the problem already -but not stopped having troubles over here hahaha-

Reading more carefully LPC1768 Datasheet, found that if SDA line is pulled low, I can recover sending manually clock pulses over SCL line. You can see it on the page 472, chapter 19.9.7.4  "I2C-bus obstructed by a LOW level on SCL or SDA".

This is my recovery function (called on timeout of start, send or get byte functions)

/*********************************************************************//**
* @brief Release bus from SDA low level condition
* @param[in] I2Cx I2C peripheral selected, should be:
* - I2C0
* - I2C1
* - I2C2
* @return None
**********************************************************************/
void I2C_BusRelease(I2C_TypeDef *I2Cx){
port_pin_t I2C_SCL;
I2C_SCL.port = GPIO0;
I2C_SCL.pin = 28;

int i = 0;
U64 clock_count;

I2C_DeInit(I2Cx);

//Init P0.28 as GPIO and force a dummy clock
gpio_init(&I2C_SCL,GPIO_OUTPUT);
clock_count = os_tick_count();

while(1){
if(os_tick_count() - clock_count >= 100){
i2c_toggle_clk(&I2C_SCL);
i++;
clock_count = os_tick_count();
if(i == 16){
break;
}
}
}
//re-init I2C0
I2C_Init(I2Cx, I2C_ADDRESS);
I2C_Cmd(I2Cx, ENABLE);
}

void i2c_toggle_clk(port_pin_t *pin){
if(gpio_get(pin) == TRUE){
gpio_clear(pin);
} else {
gpio_set(pin);
}
}

On the other hand, I have another problems related with the communication. Specifically with the the first five or four packages. Should I post a new question? Thank you! 

1,824 Views
danielholala
Senior Contributor II

Thanks for sharing your solution. That's helpful for handling a misbehaving I2C client. :-)

I wonder why you used  lpc17xx_i2c.c from a  I2C firmware library on LPC17xx that dates back from 2010? Current version of LPCOpen for LPC17xx is from 2014. Also have a look at my code change suggestions to handle an I2C bus which is stuck with low SDA or SCL lines.

Happy holidays

Daniel

0 Kudos
1,824 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Exequiel Beker,

  Thank you for your question information update.

  For the new question, please create a new post, then post the the according I2C wave and the code or project.

  Then we will help you in the new post.


Have a great day,
Kerry

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos