2.1 Private I3C/I2C Write Transactions using DMA

For a Private I3C/I2C Write transaction, it is recommended for the user to configure the DMA so that the data is transferred from the I3CxRXB register to a software buffer in the GPR space. The DMA can be triggered every time a data byte is received in the I3CxRXB register, which is available as a system level I3CxRXIF Receive Interrupt. It is not required to enable the system level I3CxRXIF interrupt for it to be used as a DMA trigger.

The DMA can be configured to transfer a set number of bytes to read from the I3CxRXB register. If it is possible that the controller might send less data bytes than that, the DMA can optionally be configured to be stopped when a TCOMPIF interrupt flag is observed. The TCOMPIF interrupt is available through the system level I3Cx General Interrupt. Although it is not required to enable the system level I3Cx interrupt to be used as a DMA trigger, the module level TCOMPIF interrupt must be enabled for this function.

Table 2-1. DMA Settings for Private I3C/I2C Write Transactions
DMA1 ParameterSetting
SSASource Address&I3CxRXB
SSZSource Size1
SMRSource Memory RegionSFR
SMODESource Address ModeSPTR remains unchanged
DSADestination AddressBeginning of software receive buffer
DSZDestination SizeNumber of bytes to read
DMODEDestination Address ModeDPTR is incremented
SIRQENStart Trigger EnableYes (enable when ready to receive data)
SIRQStart TriggerI3CxRX (I3CxRXB is full)
SSTPSource Counter Reload StopNo – SIRQEN not cleared
DSTPDestination Counter Reload StopYes – SIRQEN is cleared when DCNT reloads
AIRQENAbort Trigger EnableNo (stops when SCNT reloads)(1)
AIRQAbort TriggerN/A(1)
Note:
  1. A TCOMPIF-based abort trigger (through the system level I3Cx General Interrupt flag) can optionally be set to abort the DMA when the Controller ends the transaction prematurely.

How It Works:

  1. After SIRQEN is set, the DMA is triggered as soon as data are available if the I3CxRXIF interrupt flag is set and the content of the I3CxRXB register is transferred to the first address in the software receive buffer. DPTR is incremented and now points to the following address in the software receive buffer. Because SSZ = 1, the DMA stops after the first transfer. Since SSTP = 0, SIRQEN remains set, and DMA waits for the next trigger.
  2. When the data is available in I3CxRXB register again, the events in step 1 are repeated.
  3. After the desired number of bytes have been transferred by the DMA (DSZ value), the DPTR reaches the end of the allocated software buffer, and DCNT reloads. Since DSTP = 1, SIRQEN is cleared, and no further DMA transfers happen.
  4. Optionally, if an abort trigger is configured using TCOMPIF, the DMA is aborted when the transaction is completed on the bus, regardless of the number of bytes transferred, which clears the SIRQEN bit.

Example Code for Private I3C/I2C Write Using DMA

// Example code for PIC18-Q20 device family

uint8_t rxData[SWBUF_SIZE];

// Private Write DMA1
void DMA1_Initialize(void)
{
    // Source and Destination settings
    DMASELECT = 0;              // DMA Instance
    DMAnSSA = &I3C1RXB;         // Source Address: Register in SFR space
    DMAnDSA = &rxData;          // Destination Address: Variable in GPR space
    DMAnSSZ = 1;                // Source Size: Source is only one register
    DMAnDSZ = SWBUF_SIZE;       // Destination Size: Size of variable in GPR space
    DMAnSIRQ = 0x40;            // Start Trigger: I3C1RX
    DMAnAIRQ = 0x00;            // Abort Trigger: None
    
    DMAnCON1bits.SMODE = 0b00;  // Source Mode: Pointer unchanged
    DMAnCON1bits.SSTP = 0;      // Source Reload: No; SIRQEN not cleared
    DMAnCON1bits.SMR = 0b00;    // Source Region: SFR/GPR space
    DMAnCON1bits.DMODE = 0b01;  // Destination Mode: Pointer incremented
    DMAnCON1bits.DSTP = 1;      // Destination Reload: Yes; SIRQEN is cleared
    
    DMAnCON0bits.SIRQEN = 0;    // Start Trigger: Disabled (enable later when ready)
    DMAnCON0bits.AIRQEN = 0;    // Abort Trigger: Disabled (stops when SCNT reloads)
    DMAnCON0bits.DGO = 0;       // DMA Transaction: Do not start transaction yet
    
    // Clear interrupt flags
    PIR0bits.DMA1DCNTIF = 0;    // Destination Count Interrupt
    PIR0bits.DMA1SCNTIF = 0;    // Source Count Interrupt
    PIR0bits.DMA1AIF = 0;       // Abort Interrupt
    PIR0bits.DMA1ORIF = 0;      // Overrun Interrupt
    
    // Enable appropriate interrupts as needed
    PIE0bits.DMA1DCNTIE = 1;    // Destination Count Interrupt (enabled)
    PIE0bits.DMA1SCNTIE = 0;    // Source Count Interrupt
    PIE0bits.DMA1AIE = 0;       // Abort Interrupt
    PIE0bits.DMA1ORIE = 0;      // Overrun Interrupt
	
    //Enable DMA
    DMAnCON0bits.EN = 1;
	
}

void I3C1_ReceiveDataWithDMA(void)
{
    // Clear RXB+RXFIFO if appropriate
    I3C1CON0bits.CLRRXB = 1;

    // Enable One-shot ACK (if ACKP = NACK by default)
    I3C1CON1bits.ACKPOS = 1;

    // Start DMA transfer with trigger
    DMASELECT = 0;
    DMAnCON0bits.SIRQEN = 1;
}

void __interrupt(irq(IRQ_DMA1DCNT)) DMA1_DCNT_ISR(void)
{
    PIR0bits.DMA1DCNTIF = 0;
    
    // Code execution reaches here when DMA has completed transfer
    // Received data is now available for processing in rxData[]
}