2012-12-13 10:18 AM
Hello Community,
A peculiar situation with the I2C has caught me off guard, Some Background for you:- System Details:- 1) Hardware: STM32F103T8U7 communicating with an Atmel ATTiny4313 over the I2C Bus. 2) The STM32 is always the Master in the scheme. 3) Using DMA on the ST side. 4) Software: The DMA driver is based on the Application node AN2824 (June 2010 Doc ID 15021 Rev 4) Specifically, my send function is directly based on the send function in the following source file:- * @file OptimizedI2Cexamples/src/I2CRoutines.c * @author MCD Application Team * @version V4.0.0 * @date 06/18/2010 Observed Behavior:- When I use DMA to transmit a single byte, everything is ok. However, for transmissions greater than 1 byte, I observe an extra byte being sent. This ''ghost'' byte is easily observed on the scope and can be received at the slave as well. Just to be cautious, I checked the value of the data in the next contiguous memory location as my tx buffer thinking maybe I am DMA-ing more than expected but this value is totally different. Another curious thing is that the ''ghost'' byte follows a pattern with each send and repeats. Here is what the send function looks like:-**
* Send data on the I2C bus using DMA(Master Transmits)
*
* \param port private data
* \param data what to send
* \param length how much to send
* \return how much was sent (negative on error)
*****************************************************************************/
int16_t HAL_I2C_Send_DMA_DIRECT(const void *port, uint8_t *data, const int16_t length)
{
int16_t result = OK;
__IO uint32_t Timeout = 0;
__IO uint32_t temp = 0;
STM32_I2C_t *self = (STM32_I2C_t *)port;
uint8_t SlaveAddress = (0x10<<
1
);
// configure the port for this interface
STM32_I2C_Open(self);
while((I2C_GetFlagStatus(self->port, STM32_I2C1_FLAG_BUSY_EVENT)) && (Timeout-- > 0));
/* Enable Error IT (used in all modes: DMA, Polling and Interrupts */
self->port->CR2 |= I2C_IT_ERR;
Timeout = 0xFFFF;
/* Configure the DMA channel for I2Cx transmission */
I2C_DMAConfig (self->port, data, length, I2C_DIRECTION_TX);
/* Enable the I2Cx DMA requests */
self->port->CR2 |= CR2_DMAEN_Set;
/* Send START condition */
self->port->CR1 |= CR1_START_Set;
/* Wait until SB flag is set: EV5 */
while ((self->port->SR1&0x0001) != 0x0001)
{
if (Timeout-- == 0)
return Error;
}
Timeout = 0xFFFF;
/* Send slave address */
/* Reset the address bit0 for write */
SlaveAddress &= OAR1_ADD0_Reset;
/* Send the slave address */
self->port->DR = SlaveAddress;
/* Wait until ADDR is set: EV6 */
while ((self->port->SR1&0x0002) != 0x0002)
{
if (Timeout-- == 0)
return Error;
}
/* Clear ADDR flag by reading SR2 register */
temp = self->port->SR2;
if (self->port == I2C1)
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC6));
/* Disable the DMA1 Channel 6 */
DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC6);
}
else /* self->port = I2C2 */
{
/* Wait until DMA end of transfer */
while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));
/* Disable the DMA1 Channel 4 */
DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
/* Clear the DMA Transfer complete flag */
DMA_ClearFlag(DMA1_FLAG_TC4);
}
/* EV8_2: Wait until BTF is set before programming the STOP */
while ((self->port->SR1 & 0x00004) != 0x000004);
/* Program the STOP */
self->port->CR1 |= CR1_STOP_Set;
/* Make sure that the STOP bit is cleared by Hardware */
while ((self->port->CR1&0x200) == 0x200);
STM32_I2C_Close(self);
return result;
}
The DMA configuration is also based on the AN2824 supplied optimized examples I mention above. I will refrain from posting them here but can provide those too if found relevant. Any Thoughts ?
Thanks,
Erratosthenes
#i2c-send #i2c #dma #extra-byte
2012-12-13 10:23 AM
Also, the
STM32_I2C_Open and
STM32_I2C_Close
functions merely init the I2C peripheral before transmit. They were added to add modularity to the driver so that any device behaving like the master could use these commands without being concerned about I2C configuration.2012-12-14 11:42 AM
Thank you all for looking at this.
I have found the offending piece of code deep within my I2C driver that caused this strange behavior. My driver was based on an earlier architecture that used a different microcontroller and it had a specific artifact due to that. In short, I was software resetting the I2C peripheral on my master after every transaction (send and receive). Once I took it out, everything went normal.