AnsweredAssumed Answered

STM32F103 I2C Send using DMA adds extra 'ghost' byte.

Question asked by Erratosthenes on Dec 13, 2012
Latest reply on Dec 14, 2012 by Erratosthenes
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
  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 */
      /* Clear the DMA Transfer complete flag */
  else  /* self->port = I2C2 */
      /* Wait until DMA end of transfer */
      while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));
      /* Disable the DMA1 Channel 4 */
      /* Clear the DMA Transfer complete flag */
  /* 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);
  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 ?