AnsweredAssumed Answered

I2C with DMA, strange byte counter behavior

Question asked by age on Apr 14, 2014
Latest reply on Apr 17, 2014 by age

Hello!

 

I'm working on a DMA-enabled I2C driver for MKL25Z (Freedom Board). My problem is this: first, I coufigure DMA to send two bytes; then I start a DMA/I2C transfer by writing the slave address to I2C Data register; after the transfer completes (DMA interrupt fires), only one byte of the two appears to have been sent (total 2/3: slave address and first data byte).

I verify this a) by watching I2C SCL with a scope; b) by comparing behavior with DMA-less I2C code -- I configure an accelerometer to generate interrupts.

 

However, if I write N + 1 to DMA BCR where N is the number of bytes to be transferred, the correct number of bytes is transferred.

Also, errata id 5746 for mask 1N97F seems remotely related: "When using the PIT to trigger DMA transfers using cycle steal mode, two data transfers per request are generated".

 

I'd much appreciate a solution to this.

 

The setup code is this:

      SIM->SCGC4 |= SIM_SCGC4_I2C0;
      SIM->SCGC4 |= SIM_SCGC5_PORTE;
// enable DMA and DMAMUX clocks
      SIM->SCGC7 |= 1 << 8;
      SIM->SCGC6 |= 1 << 1;
// I2C SDA and SCL
      PORTE->PCR[24] = PORTx_PCRn_MUX(5);
      PORTE->PCR[25] = PORTx_PCRn_MUX(5);
    
      I2C0->F = 0x20;
// enable interrupts and DMA, all interrupt conditions generate DMA requests
      I2C0->C1 = I2Cx_C1_IICEN | I2Cx_C1_DMAEN | I2Cx_C1_IICIE;
// enable channel 0 with 8 bit source and dest, cycle-steal mode (one byte per I2C request)
      DMA->ch[0].DCR = DMA_DCRn_SSIZE(1) | DMA_DCRn_DSIZE(1) | DMA_DCRn_CS
        | DMA_DCRn_EINT | DMA_DCRn_D_REQ;
// DMAMUX ch. 0 with I2C0 as source
      DMAMUX->CHCFG[0] = DMAMUX_CHCFGn_ENBL | DMAMUX_CHCFGn_SOURCE(22);

 

Transmission code:

    DMA->ch[0].DSR_BCR = DMA_DSR_BCRn_BCR(txbytes + 1); // <--- txbytes = 2; if I leave the +1 out, only one byte is transferred
    DMA->ch[0].SAR = txbuf; // a buffer with two bytes
    DMA->ch[0].DAR = &I2C0->D;
    DMA->ch[0].DCR |= DMA_DCRn_SINC | DMA_DCRn_ERQ; // increment source address, listen to requests from I2C
    
// transmission/master mode
    I2C0->C1 |= I2Cx_C1_TX;
    I2C0->C1 |= I2Cx_C1_MST;
// writing slave address; when this completes, TCF flag is set, IICIF is set, DMA transaction begins
    I2C0->D = (addr << 1);
// wait until DMA interrupt has fired

 

DMA interrupt handler code:

  DMA->ch[0].DSR_BCR |= DMA_DSR_BCRn_DONE; // clear all status flags

Outcomes