AnsweredAssumed Answered

MQX Kinetis I2C DMA implementation for multi byte transfer

Question asked by annamol alex on Sep 12, 2017

Hi,

 

I have a custom board with 4xAFE4404 mapped to K65. My development environment is IAR and MQX4.2. The application is supposed to read data continuously from these sensors and push data achieving around 1Ksps from each channel. Individual AFE4404 read is equivalent to 48 bytes which comprises of slave address, register address, followed by device address and 3 bytes of data. This takes around 130us. My I2C clock is running at 400KHz. AFE4404 has a maximum clock of 400KHz and even K65 has maximum clock of 400KHz. Data rate is coming around 500-600sps as of now. To reduce the time consumed by I2C transaction the following changes were attempted.

  1. Tried increasing clock speed till 1MHz thinking it supports fast I2C mode, but no luck
  2. Tried with DMA implementation as per reference manual. Couldn't get the implementation for multi byte transfer. Is there any MQX based implementation which uses DMA?
  3. In my current implementation using DMA, 1 byte of data after a dummy read gets copied on to the destination buffer. . Even if I alter the major loop count, code either gets stuck or no transfer happens.
  4. Is DMA based implementation possible for multi byte I2C transfer? if yes, is there any sample project?
  5. Even if it is DMA based implementation, do we have to perform an I2C register read (for 3 data bytes) to clock out the data and use DMA only for reception?

I have attached the DMA section I was using and AFE4404 read code, please let me know if I am doing something wrong with configurations?

 

if((afe4404_data_ready_flag_4==true))
      {          
         DMA_RX_Init(DMA_BASE_PTR,  18, 0,(uint32_t)(&(I2C1_D)),(uint32_t)&dma_data, 3, 1 ); //DMA SLAVE
        I2C_C1_REG(I2C1_BASE_PTR) |= I2C_C1_DMAEN_MASK;       
        DMA_ERQ |= (DMA_ERQ_ERQ0_MASK | DMA_ERQ_ERQ1_MASK);
        Reflectance_raw_IR_1 = afe4404_register_read_dma(AFE4404_PRPCT,afe4404_1,3);

.}

 

void DMA_RX_Init(DMA_MemMapPtr DMA_Channel, uint8_t dma_source, int index, uint32_t source_addr, uint32_t dest_addr, uint32_t num_bytes, uint32_t bytes_per_xfer){
  /* num_bytes is total bytes */
  /* setup depends on how many bytes per transfer */
  uint32_t      citer_biter = (num_bytes-1) / bytes_per_xfer;
  uint32_t      ssize_dsize_attr;
  uint8_t ack_nak_array[3];
 
  uint8_t data[3]; // to hold data
 
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

 

  for(int i=0;i<3;i++)
    data[i]=0x00;
 
  DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR,index)  = 0x00;  
  /* setup ssize and dsize parameter for TCD ATTR register based on transfer size assigned above */
  /* 0:8bit; 1:16-bit, 2:32-bit, 5:16-byte, 6:32-byte */
  switch(bytes_per_xfer)
  {
    case 1: default:
      ssize_dsize_attr = 0;  break;       /* 8-bit */
    case 2:
      ssize_dsize_attr = 1;  break;       /* 16-bit */
    case 4:
      ssize_dsize_attr = 2;  break;       /* 32-bit */
    case 16:
      ssize_dsize_attr = 5;  break;       /* 16-byte */
    case 32:
      ssize_dsize_attr = 6;  break;       /* 32-byte */
  }     
  DMA_SADDR_REG(DMA_Channel, index)          = (uint32_t)&I2C1_D;;                                        // Source address
  DMA_SOFF_REG(DMA_Channel, index)           = 0x0000;                                             // Source address increments 0 bytes (uint32)
  DMA_SLAST_REG(DMA_Channel, index)          = 0;                                                  // After the major loop ends the source address decrements the size of the buffer
  DMA_DADDR_REG(DMA_Channel, index)          = dest_addr;                                          // Destination address
  DMA_DOFF_REG(DMA_Channel, index)           = bytes_per_xfer;                                     // Destination offset increments
  DMA_DLAST_SGA_REG(DMA_Channel, index)      = -num_bytes;                                         // Destination address shift, go to back to beginning of buffer
  DMA_NBYTES_MLNO_REG(DMA_Channel, index)    = bytes_per_xfer;                                     // The minor loop moves by bytes per transfer
  DMA_BITER_ELINKNO_REG(DMA_Channel, index)  = citer_biter;                                        // Major loop iterations
  DMA_CITER_ELINKNO_REG(DMA_Channel, index)  = citer_biter;                                        // Set current interation count  
  DMA_ATTR_REG(DMA_Channel, index)           = (DMA_ATTR_SSIZE(ssize_dsize_attr)|DMA_ATTR_DSIZE(ssize_dsize_attr));   // Source a destination size 0:8bit; 1:16-bit, 2:32-bit, 5:16-byte, 6:32-byte
  DMA_CSR_REG(DMA_Channel, index)            = DMA_CSR_INTMAJOR_MASK | DMA_CSR_DREQ_MASK;          // Enable end of loop DMA interrupt, disable request at end of major iteration              
      for( int j=0; j <3 ; j++)
      {
          if(j >= (1))
          {
              ack_nak_array[j] = 0xA1 | I2C_C1_TXAK_MASK;
          }
          else
          {
              ack_nak_array[j] = 0xA1 & (~I2C_C1_TXAK_MASK);
          }
      }
 
  DMA_SADDR_REG(DMA_Channel, 1 )             =  (uint32_t)ack_nak_array;  ;                                        // Source address
  DMA_SOFF_REG(DMA_Channel, 1)           = 0x0000;                                             // Source address increments 0 bytes (uint32)
  DMA_SLAST_REG(DMA_Channel, 1)          = 0;                                                  // After the major loop ends the source address decrements the size of the buffer
  DMA_DADDR_REG(DMA_Channel, 1)          = (uint32_t)&I2C_C1_REG(I2C1_BASE_PTR);                                          // Destination address
  DMA_DOFF_REG(DMA_Channel, 1)           = bytes_per_xfer;                                     // Destination offset increments
  DMA_DLAST_SGA_REG(DMA_Channel, 1)      = -num_bytes;                                         // Destination address shift, go to back to beginning of buffer
  DMA_NBYTES_MLNO_REG(DMA_Channel, 1)    = bytes_per_xfer;                                     // The minor loop moves by bytes per transfer
  DMA_BITER_ELINKNO_REG(DMA_Channel, 1)  = citer_biter;                                        // Major loop iterations
  DMA_CITER_ELINKNO_REG(DMA_Channel, 1)  = citer_biter;                                        // Set current interation count  
  DMA_ATTR_REG(DMA_Channel, 1)           = (DMA_ATTR_SSIZE(ssize_dsize_attr)|DMA_ATTR_DSIZE(ssize_dsize_attr));   // Source a destination size 0:8bit; 1:16-bit, 2:32-bit, 5:16-byte, 6:32-byte
  DMA_CSR_REG(DMA_Channel, 1)            = DMA_CSR_INTMAJOR_MASK | DMA_CSR_DREQ_MASK;          // Enable end of loop DMA interrupt, disable request at end of major iteration          
 
 DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR,0)    = (DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(dma_source));     // DMA source DMA Mux to tie source to DMA channel
}

 

void DMA_TX_Init(DMA_MemMapPtr DMA_Channel, uint8_t dma_source, int index, uint32_t source_addr, uint32_t dest_addr, uint32_t num_bytes, uint32_t bytes_per_xfer) {

 

/* num_bytes is total bytes */
  /* setup depends on how many bytes per transfer */
  uint32_t      citer_biter = num_bytes / bytes_per_xfer;
  uint32_t      ssize_dsize_attr;
 
  switch(bytes_per_xfer)
  {
    case 1: default:
      ssize_dsize_attr = 0;  break;       /* 8-bit */
    case 2:
      ssize_dsize_attr = 1;  break;       /* 16-bit */
    case 4:
      ssize_dsize_attr = 2;  break;       /* 32-bit */
    case 16:
      ssize_dsize_attr = 5;  break;       /* 16-byte */
    case 32:
      ssize_dsize_attr = 6;  break;       /* 32-byte */
  }
      

 

  DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR,index)    = (DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(dma_source));     // DMA source DMA Mux
  DMA_SADDR_REG(DMA_Channel, index)          = source_addr;                                        // Source address
  DMA_SOFF_REG(DMA_Channel, index)           = bytes_per_xfer;                                     // Source address increments by number of bytes per transfer
  DMA_SLAST_REG(DMA_Channel, index)          = -num_bytes;                                         // After the major loop ends, reset pointer to beginning of buffer
  DMA_DADDR_REG(DMA_Channel, index)          = dest_addr ;                                         // Destination address
  DMA_DOFF_REG(DMA_Channel, index)           = 0x0;                                                // Destination offset increments 0 bytes (uint32)
  DMA_DLAST_SGA_REG(DMA_Channel, index)       = 0;                                                 // Destination address shift
  DMA_NBYTES_MLNO_REG(DMA_Channel, index)    = bytes_per_xfer;                                     // The minor loop moves 32 bytes per transfer
  DMA_BITER_ELINKNO_REG(DMA_Channel, index)  = citer_biter;                                        // Major loop iterations
  DMA_CITER_ELINKNO_REG(DMA_Channel, index)  = citer_biter;                                        // Set current interation count  
  DMA_ATTR_REG(DMA_Channel, index)           = (DMA_ATTR_SSIZE(ssize_dsize_attr) | DMA_ATTR_DSIZE(ssize_dsize_attr));   // Source a destination size 0:8bit; 1:16-bit, 2:32-bit, 5:16-byte, 6:32-byte
 
  DMA_CSR_REG(DMA_Channel, index)            = DMA_CSR_INTMAJOR_MASK | DMA_CSR_DREQ_MASK;          // Enable end of loop DMA interrupt; clear ERQ @ end of major iteration           

}

 

signed long afe4404_register_read_dma(unsigned char Reg_address,MQX_FILE_PTR fd,int n)
{
  unsigned char configData[3]={0};                                                          // Local variable to hold data read from AFE4404 registaer
  unsigned char reg_address[2];                                                            // Local variable to hold register address
  signed long retVal=0;                                                                   // Local variable to hold the 24 bit data read from registers                                                                  
  _mqx_int param,result;                                                                  // Local variable to hold status of transaction
  int count=0;                                                                            // Local variable to hold number of bytes transferred via I2C
  (void)count;
  // Creating packet for transfer
  reg_address[0]=Reg_address;                                                           // Copying register address from which value needs to be read
  // Set I2C slave address
   param = AFE4404_ADDRESS;
#ifndef DEBUG_PRINTF
   printf ("  Set I2C bus address to 0x%02x ... ", param);
#endif
   if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &param))
      printf ("04_ERROR\n");
   
   // Write register read command via I2C
   count = fwrite(reg_address,1,1,fd);

 

   // Wait for completion
   result = fflush (fd);

 

    // Restart I2C transfer for reading
   if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_REPEATED_START, NULL))
      printf ("04_ERROR\n");
   // Set read request for n number of bytes
   param = n;
   if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_SET_RX_REQUEST, &param))
      printf ("04_ERROR\n");    
   // Read all data
//   result = fread (configData, 1, 3, fd);
////   if (result != n)
////      printf ("04_ERROR\n");
 
   DMA_CSR_REG(DMA_BASE_PTR, 0)            |= DMA_CSR_START_MASK;     // Enable end of loop DMA interrupt, disable request at end of major iteration         
    I2C_C1_REG(I2C1_BASE_PTR) |= I2C_C1_DMAEN_MASK;  
    for(int temp=0; temp<5050; temp++);   

    I2C_C1_REG(I2C1_BASE_PTR) &= ~(I2C_C1_DMAEN_MASK | I2C_C1_TX_MASK);       
   // Stop I2C transfer
   if (I2C_OK != ioctl (fd, IO_IOCTL_I2C_STOP, NULL))
      printf ("04_ERROR\n");
   
//   if(configData[0]!=0xFF)
//   {
//     // Creating 24 bit return value
//     retVal = configData[0];
//     retVal = (retVal << 8) | configData[1];
//     retVal = (retVal << 8) | configData[2];
//   }
//   
//   else
//   { // to avoid larger spikes
//      configData[0]=0;
//      retVal=configData[0];
//     retVal = (retVal << 8) | configData[1];
//     retVal = (retVal << 8) | configData[2];
//   }
//   
//  if (Reg_address >= 0x2A && Reg_address <= 0x2F)
//  {
//    if (retVal & 0x00200000)     // check if the ADC value is positive or negative
//    {
//      retVal &= 0x003FFFFF;        // convert it to a 22 bit value
//      return (retVal^0xFFC00000);
//    }
//  }
//  return retVal;
}

Outcomes