SSDA014 September   2026 MSPM0G5117 , MSPM0G5187

 

  1.   1
  2. 1 Description
  3. 2 Required Peripherals
  4. 3 Compatible Devices
  5. 4 Design Steps
  6. 5 Design Considerations
  7. 6 Software Flowchart
  8. 7 Application Code
  9. 8 Data Flow Examples
  10. 9 Additional Resources
  11. 10Trademarks

Application Code

USB to I2C:


/********************************************************
* Function name  : usb2i2c_pushTask
* Description    : USB -> I2C. Read data from USB CDC interface and push
*                  into gUSB2I2CTask queue. Each task holds one USB frame
*                  (up to 64 bytes). Called from tud_cdc_rx_cb callback.
* Parameter      : None (modifies gUSB2I2CTask)
* Return         : None
**********************************************************/
void usb2i2c_pushTask(void)
{
    // Check space for new task
    if(gUSB2I2CTask.fifoCount >= I2C_TX_BUF_SIZE) return;

    // Get current task slot in queue
    TxElement_t *pTask = &gUSB2I2CTask.fifoPointer[gUSB2I2CTask.fifoIn];
    
    // Read up to 64 bytes from USB CDC
    uint32_t cnt = tud_cdc_n_read(0, pTask->buffer, 64);
    if(cnt == 0) return;                    // Skip if no data

    pTask->length = cnt;

    // Advance queue pointer and mark task as ready
    fifoCompleteWrite(&gUSB2I2CTask, I2C_TX_BUF_SIZE);
}
/********************************************************
* Function name  : usb2i2c_popTask
* Description    : USB -> I2C. Pop task from queue and transmit data through
*                  I2C to slave device. Waits if I2C TX already in progress.
*                  Called from main loop when fifoCount > 0.
* Parameter      : None (modifies gUSB2I2CTask, i2cInProgress)
* Return         : None
**********************************************************/
void usb2i2c_popTask(void)
{
    if(i2cInProgress) return;

    if(gUSB2I2CTask.fifoCount == 0) return;
    
    // Current process task
    TxElement_t *pTask = &gUSB2I2CTask.fifoPointer[gUSB2I2CTask.fifoOut];

    /* Setup DMA and start I2C write */
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(pTask->buffer));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&I2C_0_INST->i2cc->TXDATA));
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, pTask->length);
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);

    DL_I2CC_startTransfer(I2C_0_INST, I2C_ADDRESS, DL_I2CC_DIRECTION_TX, pTask->length);

    i2cInProgress = true;
}

I2C to USB:

/********************************************************
* Function name  : i2c2usb_pushTask
* Description    : I2C -> USB. Read data from I2C slave and queue for USB transmission
*                  Triggered when slave device signals data ready via GPIO pin.
*                  Called from GPIO interrupt handler.
* Parameter      : None (modifies gI2C2USBTask)
* Return         : None
**********************************************************/
void i2c2usb_pushTask(void)
{
    // Check space for new task
    if (gI2C2USBTask.fifoCount >= USB_TX_BUF_SIZE) return;
    if(i2cInProgress) return;

    // Current process task
    TxElement_t *pTask = &gI2C2USBTask.fifoPointer[gI2C2USBTask.fifoIn];
    pTask->length = USB_FRAME_PACKAGE;

    /* Setup DMA and initiate I2C read */
    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&I2C_0_INST->i2cc->RXDATA));
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(pTask->buffer));
    DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, USB_FRAME_PACKAGE);
    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);

    DL_I2CC_startTransfer(I2C_0_INST, I2C_ADDRESS, DL_I2CC_DIRECTION_RX, USB_FRAME_PACKAGE);

    i2cInProgress = true;
}
/********************************************************
* Function name  : i2c2usb_popTask
* Description    : I2C -> USB. Pop task from queue and transmit data through
*                  USB CDC interface. All data in one task is sent in same USB
*                  frame. Checks if USB TX buffer has space. Called from main
*                  loop when fifoCount > 0.
* Parameter      : None (modifies gI2C2USBTask)
* Return         : None
**********************************************************/
void i2c2usb_popTask(void)
{
    if(gI2C2USBTask.fifoCount == 0) return;   // Queue empty

    TxElement_t *pTask = &gI2C2USBTask.fifoPointer[gI2C2USBTask.fifoOut];

    if(tud_cdc_n_write_available(0) >= pTask->length){  // USB FIFO has space
        uint32_t written = tud_cdc_n_write(0, pTask->buffer, pTask->length);

        if(written == pTask->length){                   // Entire task sent atomically
            tud_cdc_n_write_flush(0);
            fifoCompleteRead(&gI2C2USBTask, USB_TX_BUF_SIZE);
        }
    }
}

I2C Interrupt Handler:

//--------------------------------------------------------------------+
// I2C Interrupt Handler
//--------------------------------------------------------------------+
void I2C_0_INST_IRQHandler(void) {
    switch(DL_I2CC_getPendingInterrupt(I2C_0_INST)) {
        case DL_I2CC_IIDX_EVENT1_DMA_DONE:
            // TX: DMA transfer to I2C FIFO complete
            // IMPORTANT: Don't pop task yet - STOP condition still in progress
            DL_I2CC_clearInterruptStatus(I2C_0_INST, DL_I2CC_INTERRUPT_STOP);
            DL_I2CC_enableInterrupt(I2C_0_INST, DL_I2CC_INTERRUPT_STOP);
            break;

        case DL_I2CC_IIDX_STOP:
            // TX complete: I2C STOP condition detected - transmission finished
            // Only enabled when I2C Host write
            DL_I2CC_disableInterrupt(I2C_0_INST, DL_I2CC_INTERRUPT_STOP);
            fifoCompleteRead(&gUSB2I2CTask, I2C_TX_BUF_SIZE);
            i2cInProgress = false;
            break;

        case DL_I2CC_IIDX_EVENT2_DMA_DONE:
            // RX complete: DMA received 64 bytes from I2C slave
            fifoCompleteWrite(&gI2C2USBTask, USB_TX_BUF_SIZE);
            i2ctargetDataReady = false;  // Clear slave notification flag
            i2cInProgress = false;
            break;

        case DL_I2CC_IIDX_NACK:
            // I2C Error: Slave did not acknowledge address
            i2cInProgress = false;
            __BKPT(0);  // Halt for debugging - indicates I2C communication failure
            break;

        default:
            break;
    }
}

Overflow Handling:

void tud_cdc_rx_cb(uint8_t itf)
{
    if (gUSB2I2CTask.fifoCount < I2C_TX_BUF_SIZE) {
        usb2i2c_pushTask();
    } else {
        /* Queue full: discard to unblock TinyUSB RX buffer */
        uint8_t dummyBuf[64];
        tud_cdc_n_read(itf, dummyBuf, 64);
    }
}