I have become ensnared in an interesting I2C phenomenon which I hope one better versed in such things than I might be able to shed some light.
I am using MCUXpresso IDE v11.3.0 [Build 5222] and the lpcxpresso55s16 SDK. I am writing test code for our own hardware but the problematic code is drawn straight from the SDK sample project lpcxpresso55s16_i2c_polling_b2b_master.
First, I am trying to interface with a Vishay 6030 Ambient Light Sensor. To read the sensor, I need to replicate the instructions outlined in Figure (1). Note that you’ve got to send the shifted slave address, then the command code, then a second shifted slave address but this time with the read bit set, after which the slave should make two data bytes available:
Next, the Figure (1) I2C sequence translates to the Figure (2) code snippet (which is shamelessly plagiarized from the SDK lpcxpresso55s16_i2c_polling_b2b_master sample project):
//send start and I2C bus slave byte and W bit…
g_tRetVal = I2C_MasterStart( FLEXCOMM5_PERIPHERAL, I2C_MASTER_SLAVE_ADDR_7BIT, kI2C_Write );
if ( g_tRetVal == kStatus_Success )
//send Command code...
(06) g_tRetVal = I2C_MasterWriteBlocking( FLEXCOMM5_PERIPHERAL, &(g_u8MasterBuffTX[ 0 ]), 1, kI2C_TransferNoStopFlag);
(07) if ( g_tRetVal == kStatus_Success )
(09) //send second start, I2C bus slave address byte, and R bit...
(10) g_tRetVal = I2C_MasterRepeatedStart( FLEXCOMM5_PERIPHERAL, I2C_MASTER_SLAVE_ADDR_7BIT, kI2C_Read );
Alas! Running the Figure (2) code yields an “arbitration” error, so it’s haul-out-the-oscilloscope time to see what’s going down on the SDA and SCL lines. The results are shown in Figure (3):
Interesting…! Cross referencing the Figure (2) code with the Figure (3) oscilloscope trace, the following is happening (even before the arbitration error:
How can this be?
Well first lets check out the kI2C read/write enum. Indeed, the read and write values are distinct:
/*! @brief Direction of master and slave transfers. */
typedef enum _i2c_direction
kI2C_Write = 0U, /*!< Master transmit. */
kI2C_Read = 1U /*!< Master receive. */
Next, how about the I2C_MasterRepeatedStart( ) function? It turns out that the I2C_MasterRepeatedStart() just straight out calls the I2C_MasterStart() function so let’s look at that (see Fig (4) below):
* @brief Sends a REPEATED START on the I2C bus.
* @param base I2C peripheral base pointer
* @param address 7-bit slave device address.
* @param direction Master transfer directions(transmit/receive).
* @retval kStatus_Success Successfully send the start signal.
* @retval kStatus_I2C_Busy Current bus is busy but not occupied by current I2C master.
static inline status_t I2C_MasterRepeatedStart(I2C_Type *base, uint8_t address, i2c_direction_t direction)
return I2C_MasterStart(base, address, direction);
* brief Sends a START on the I2C bus.
* This function is used to initiate a new master mode transfer by sending the START signal.
* The slave address is sent following the I2C START signal.
* param base I2C peripheral base pointer
* param address 7-bit slave device address.
* param direction Master transfer directions(transmit/receive).
* retval kStatus_Success Successfully send the start signal.
* retval kStatus_I2C_Busy Current bus is busy.
(01) status_t I2C_MasterStart(I2C_Type *base, uint8_t address, i2c_direction_t direction)
(03) uint32_t result;
(04) result = I2C_PendingStatusWait(base);
(05) if (result == (uint32_t)kStatus_I2C_Timeout)
(07) return kStatus_I2C_Timeout;
(10) /* Write Address and RW bit to data register */
(11) base->MSTDAT = ((uint32_t)address << 1) | ((uint32_t)direction & 1U);
(12) /* Start the transfer */
(13) base->MSTCTL = I2C_MSTCTL_MSTSTART_MASK;
(15) return kStatus_Success;
To summarize Fig 2 line 10 calls I2C_MasterRepeatedStart() with parameter 3, the direction, as read (i.e., 1). Function I2C_MasterRepeatedStart() calls function I2C_MasterStart() with its’ parameter 3 also as read (i.e., 1). In function I2C_MasterStart() the direction parameter is or-ed with the shifted I2C address and written to the MSTDAT register (figure 5 line 11). By the time the data hits the bus (Figure 3) the read/write bit is cleared to a write.
Can anyone see a failing in the logic here?
Thanks for your thoughts,