SLAAES5A February   2025  – August 2025 MSPM0G3507

 

  1.   1
  2. 1Design Description
  3. 2Required Peripherals
  4. 3Design Steps
  5. 4Design Considerations
  6. 5Software Flowchart
  7. 6Device Configuration
  8. 7Application Code
  9. 8Porting Guide
  10. 9Revision History
  11.   Trademarks

Application Code

The initialization of the buffers, counters, enum, and flag are shown here. To change the specific values used by the SPI and I2C maximum packet size, modify the #defines in the beginning of the document, as demonstrated in the following code block.

#include "ti_msp_dl_config.h"
/* Delay for 5ms to ensure SPI TX is idle before starting transmission */
#define SPI_TX_DELAY (160000)

/*Define max packet sizes*/
#define MAX_PACKET_SIZE 4

/*SPI Buffers & Variables*/
uint8_t gSPITxData[MAX_PACKET_SIZE];
uint8_t gSPIRxData[MAX_PACKET_SIZE];
volatile uint8_t gSPIRxCount = 0;                       // Variable to track # of bytes SPI Received
/*I2C Controller Buffers &  Variable*/
uint8_t gI2CTxData[MAX_PACKET_SIZE];
uint8_t gI2CRxData[MAX_PACKET_SIZE];
volatile uint8_t gI2CAddress = 0x48;                    // Target Address to communicate to
volatile uint8_t gI2CTxCount = 0;                       // Variable to track # of bytes I2C Transmitted
volatile uint8_t gI2CRxCount = 0;                       // Variable to track # of bytes I2C Received
volatile uint8_t rxTemp = 0;
/* Indicates status of Bridge */
enum BridgeStates {
    SPI_RX_STATE = 0,
    I2C_TX_STATE,
    I2C_RX_STATE,
    SPI_TX_STATE
} gBridgeStates;
/* Indicates status of I2C Controller */
enum I2cControllerStatus {
    I2C_C_STATUS_IDLE = 0,
    I2C_C_STATUS_TX_COMPLETE,
    I2C_STATUS_RX_STARTED,
    I2C_C_STATUS_RX_INPROGRESS,
    I2C_STATUS_RX_COMPLETE
} gI2cControllerStatus;

void bridge(void);

The main body of the application code is relatively short. First, the device peripherals and interrupts get initialized. Then, a delay occurs for the SPI TX, which is idle before starting transmission, while also the state and flag values are initialized. Following up, the main loop, which contains the bridge function, runs.

int main(void)
{
    SYSCFG_DL_init();
    /* Activate Interrupts */
    NVIC_ClearPendingIRQ(SPI_INST_INT_IRQN);
    NVIC_EnableIRQ(SPI_INST_INT_IRQN);
    NVIC_EnableIRQ(I2C_INST_INT_IRQN);

    /* Optional delay to ensure SPI TX is idle before starting transmission */
    delay_cycles(SPI_TX_DELAY);

    /*Initial states*/
    gBridgeStates = SPI_RX_STATE;
    gI2cControllerStatus = I2C_C_STATUS_IDLE;

    /* Start bridge */
    while (1) {
        bridge();   // Runs bridge
    }
}

The bridge has four states. The first state focuses on transferring the data from the SPI RX buffer to the I2C TX buffer while clearing the SPI RX buffer after the maximum package size has been received by the SPI Peripheral. The second state transmits the data from the I2C TX buffer to an I2C Target. Then, the third state makes the I2C Controller send a request data signal to the I2C target, and after recollecting the data, transfers the I2C RX Buffer to the SPI TX Buffer while clearing the I2C RX buffer. The final state fills the SPI Target with the contents of SPI TX Buffers, waits for the FIFOs to be empty (Controller asks for the data,) enables the SPI RX to Interrupt again, and restarts the bridge.

void bridge(){
    uint8_t i=0; uint8_t j=0;uint8_t k=0;                                       // Setting counter variables
    switch (gBridgeStates) {
        case SPI_RX_STATE:
            if (gSPIRxCount >= MAX_PACKET_SIZE){
                // Storing data from SPI Buffer to message that is addressed to Bridge A
                for(k = 0; k < MAX_PACKET_SIZE; k++){
                    gI2CTxData[k] = gSPIRxData[k];
                    gSPIRxData[k] = 0;
                }
                // Resetting gSPIRxCount variable
                gSPIRxCount = 0;
                gBridgeStates = I2C_TX_STATE;
            }
            else {
                break;
            }
        case I2C_TX_STATE:
            // Sending the I2C WRITE message to the 9724
            gI2CTxCount = DL_I2C_fillControllerTXFIFO(I2C_INST, &gI2CTxData[0], MAX_PACKET_SIZE);

            /* Send the packet to the target. This function will send Start + Stop automatically. */
            while (!(DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));
            DL_I2C_startControllerTransfer(I2C_INST, gI2CAddress, DL_I2C_CONTROLLER_DIRECTION_TX, MAX_PACKET_SIZE);

            /* Wait until the Controller sends all bytes AKA the I2C_C_STATUS_TX_COMPLETE to be true */
            while (gI2cControllerStatus != I2C_C_STATUS_TX_COMPLETE) {
                __WFE();
            }
            while (DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);

            gI2cControllerStatus = I2C_C_STATUS_IDLE;
            gBridgeStates = I2C_RX_STATE;                                       // Move to next Bridge stage
            break;
        case I2C_RX_STATE:
            /* Add delay between transfers */
            delay_cycles(1000);

            /* Send a read request to Target */
            gI2cControllerStatus = I2C_STATUS_RX_STARTED;
            DL_I2C_startControllerTransfer(I2C_INST, gI2CAddress, DL_I2C_CONTROLLER_DIRECTION_RX, MAX_PACKET_SIZE);

            /* Wait for all bytes to be received in interrupt */
            while (gI2cControllerStatus != I2C_STATUS_RX_COMPLETE) {
                __WFE();
            }
            while (DL_I2C_getControllerStatus(I2C_INST) &
                   DL_I2C_CONTROLLER_STATUS_BUSY_BUS);

            /* Waiting for I2C Rx buffer interrupt to happen AKA When expected package size
               is received from I2C Target */
            if(gI2CRxCount >= MAX_PACKET_SIZE){
                // Extract the received bytes from the I2C read and store them in SPI buffer #2 (Tx)
                for(j = 0; j < MAX_PACKET_SIZE; j++){
                    gSPITxData[j] = gI2CRxData[j];
                    gI2CRxData[j] = 0;
                }
                // Resetting gI2CRxCount variable
                gI2CRxCount = 0;
                gI2cControllerStatus = I2C_C_STATUS_IDLE;
                gBridgeStates = SPI_TX_STATE;                                   // Move to next Bridge stage
            }
            break;
        case SPI_TX_STATE:
            DL_SPI_fillTXFIFO8(SPI_INST, &gSPITxData[0], MAX_PACKET_SIZE);
            while(!DL_SPI_isTXFIFOEmpty(SPI_INST));

            DL_SPI_enableInterrupt(SPI_INST, DL_SPI_INTERRUPT_RX);
            gBridgeStates = SPI_RX_STATE;
            break;
    }
}

The next piece of this example is the SPI IRQ Handler. Only one interrupt is used for this example: SPI RX. When activated, the data is stored in a temporal variable and then stored in the SPI RX FIFO Buffer. Then, if the SPI RX Counter is less than the maximum package size, the SPI Rx counter increases; otherwise, the SPI RX FIFO interrupt is disabled.

void SPI_INST_IRQHandler(void)
{
    switch (DL_SPI_getPendingInterrupt(SPI_INST)) {
        case DL_SPI_IIDX_RX:
            rxTemp = DL_SPI_receiveDataBlocking8(SPI_INST);
            gSPIRxData[gSPIRxCount] = rxTemp;
            if (gSPIRxCount >= MAX_PACKET_SIZE){
                DL_SPI_disableInterrupt(SPI_INST, DL_SPI_INTERRUPT_RX);
            }else {
                gSPIRxCount++;
            }
            break;
        default:
            break;
    }
}

The final piece of code in this example is the I2C IRQ Handler. The two interrupts in this example are Controller TX Done and Controller RX FIFO Trigger. The I2C Controller Status is updated to TX Completed when TX Done is triggered. When the RX FIFO Trigger is triggered, the I2C Controller Status is updated to RX In Progress; the I2C Rx buffer receives the message stored in the I2C RX FIFO until is empty and sets the I2C Controller Status to I2C RX Complete.

void I2C_INST_IRQHandler(void)
{
    switch (DL_I2C_getPendingInterrupt(I2C_INST)) {
        case DL_I2C_IIDX_CONTROLLER_TX_DONE:
            gI2cControllerStatus = I2C_C_STATUS_TX_COMPLETE;
            break;
        case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
            gI2cControllerStatus = I2C_C_STATUS_RX_INPROGRESS;
            /* Receive all bytes from target */
            while (DL_I2C_isControllerRXFIFOEmpty(I2C_INST) != true){
                if(gI2CRxCount < MAX_PACKET_SIZE) {
                    gI2CRxData[gI2CRxCount++] = DL_I2C_receiveControllerData(I2C_INST);
                }else{
                    DL_I2C_receiveControllerData(I2C_INST);
                }
            }
            gI2cControllerStatus = I2C_STATUS_RX_COMPLETE;
            break;
        default:
            break;
    }
}