Electronic – Why does last data of I2C DMA transfer break

dmai2cstm32

I used DMA to transfer I2C data, but I always lose the last data. hardware configuration is shown below:

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)I2C1_Buffer_Tx;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = 2;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel6, &DMA_InitStructure);
  DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE); 

The data to be transfered is:

  I2C1_Buffer_Tx[0] = 0xF0;
  I2C1_Buffer_Tx[1] = 0x55;

When a data frame has been sent, an interrupt of DMA is generated:

void DMA1_Channel6_IRQHandler(void)
{
  if (DMA_GetFlagStatus(DMA1_IT_TC6) != RESET)
  {
    DMA_Cmd(DMA1_Channel6, DISABLE);
    DMA1_Channel6->CNDTR = 2;
    DMA_ClearFlag(DMA1_IT_TC6);
    switch (mode)
    {
      case 1:
        I2C_GenerateSTART(I2C1, ENABLE);
        mode = 2;
        break;
      case 2:
        I2C_GenerateSTOP(I2C1, ENABLE);
        mode = 3;
        break;......

When I set a breakpoint at I2C_GenerateSTART(I2C1, ENABLE):code sample

I get SDA signal from oscilloscope in this pattern:SDA before start

It shows that two data have been sent through SDA:0xF0 and 0x55, however, when I set breakpoint after that code:

code sample 2 and I got following data:
SDA after start

it shows that only one data has been sent through SDA: 0xF0 and then a restart condition. so, I will lose the last data if I let the whole program run as usual.

I can only explain the phenomenon like this: DMA has already sent two data(0xF0 0x55), and then it generates an interrupt. However, those two data are still in I2C's buffer, then, in DMA's interrupt, it generates a start condition. For some reason, it breaks the final data.

Can any one give some suggestion to fix this problem or I can only set buffersize of DMA n+1 if I want to send n data.

Best Answer

Your DMA Handler gets called before the last byte is sent, as this is the point in time when the DMA buffer turns empty. It has just been written into the peripherial register.

You need to wait for this byte to be out on the bus - using the I2C interrupt handler for example.