SSDA015 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 SPI:

/********************************************************
* Function name  : usb2spi_popTask
* Description    : USB -> SPI. Pop task from queue and transmit data through
*                  SPI to slave device. Waits if SPI TX already in progress.
*                  Called from main loop when fifoCount > 0.
* Parameter      : None (modifies gUSB2SPITask, spiInProgress)
* Return         : None
**********************************************************/
void usb2spi_popTask(void)
{
    if(spiInProgress) return;
    if(gUSB2SPITask.fifoCount == 0) return;

    // Current process task
    TxElement_t *pTask = &gUSB2SPITask.fifoPointer[gUSB2SPITask.fifoOut];
    
    //Flush TXFIFO
    DL_SPI_flushTXFIFO(SPI_0_INST);
    
    /* Setup DMA and start SPI write */
    DL_SPI_clearInterruptStatus(SPI_0_INST, DL_SPI_INTERRUPT_DMA_DONE_TX);
    DL_SPI_enableInterrupt(SPI_0_INST, DL_SPI_INTERRUPT_DMA_DONE_TX);
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(pTask->buffer));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->spi->TXDATA));
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, pTask->length); 
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);

    spiInProgress = true;
}
/* ===== USB RX -> SPI TX ===== */

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

    // Get current task slot in queue
    TxElement_t *pTask = &gUSB2SPITask.fifoPointer[gUSB2SPITask.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(&gUSB2SPITask, SPI_TX_BUF_SIZE);
}

SPI to USB:

/* ===== SPI RX -> USB TX ===== */

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

    // Get current task buffer
    TxElement_t *pTask = &gSPI2USBTask.fifoPointer[gSPI2USBTask.fifoIn];

    // Pre-set length since SPI master reads fixed 64 bytes per transaction
    pTask->length = USB_FRAME_PACKAGE;

    //Flush RXFIFO
    DL_SPI_flushRXFIFO(SPI_0_INST);

    //Flush TXFIFO
    DL_SPI_flushTXFIFO(SPI_0_INST);
    
    // Transmit dummy data 
    DL_SPI_disableInterrupt(SPI_0_INST, DL_SPI_INTERRUPT_DMA_DONE_TX);
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(pTask->buffer));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->spi->TXDATA));
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, USB_FRAME_PACKAGE); 
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);

    /* DMA to receive data */
    DL_SPI_flushRXFIFO(SPI_0_INST);

    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->spi->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);

    spiInProgress = true;
}
/********************************************************
* Function name  : spi2usb_popTask
* Description    : SPI -> 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 gSPI2USBTask)
* Return         : None
**********************************************************/
void spi2usb_popTask(void)
{
    if(gSPI2USBTask.fifoCount == 0) return;

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

    if(tud_cdc_n_write_available(0) >= pTask->length){
        uint32_t written = tud_cdc_n_write(0, pTask->buffer, pTask->length);

        if(written == pTask->length){
            tud_cdc_n_write_flush(0);
            fifoCompleteRead(&gSPI2USBTask, USB_TX_BUF_SIZE);
        }
    }
}

SPI Interrupt Handler:

//--------------------------------------------------------------------+
// SPI Interrupt Handler
//--------------------------------------------------------------------+
void SPI_0_INST_IRQHandler(void) {
    switch(DL_SPI_getPendingInterrupt(SPI_0_INST)) {
        case DL_SPI_IIDX_DMA_DONE_TX:
            // TX: DMA transfer to SPI FIFO complete
            DL_SPI_clearInterruptStatus(SPI_0_INST, DL_SPI_INTERRUPT_TX_EMPTY);
            DL_SPI_enableInterrupt(SPI_0_INST, DL_SPI_INTERRUPT_TX_EMPTY);
            break;

        case DL_SPI_IIDX_DMA_DONE_RX:
            // RX complete: DMA received 64 bytes from SPI slave
            fifoCompleteWrite(&gSPI2USBTask, USB_TX_BUF_SIZE);
            SPISlaveDataReady = false;  // Clear slave notification flag
            spiInProgress = false;
            break;

            // TX complete
        case DL_SPI_IIDX_TX_EMPTY:
            DL_SPI_disableInterrupt(SPI_0_INST, DL_SPI_INTERRUPT_TX_EMPTY);
            fifoCompleteRead(&gUSB2SPITask, SPI_TX_BUF_SIZE);
            spiInProgress = false;
            break;

        default:
            break;
    }
}