This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSPM0G3507: DMA-Enabled SPI Communication with ADS8684A

Part Number: MSPM0G3507
Other Parts Discussed in Thread: ADS8684A, SYSCONFIG

Tool/software:

Hello!

I am testing a design where a MSPM0G3507 (controller) communicates via SPI with an ADS8684A (peripheral).

I validated communication on each channel of the ADC individually-- successfully storing conversions. I am now working on obtaining conversions from each channel in succession, starting with Channel 0. I am doing manual instead of automatic scanning as I plan to implement averaging later on.

To achieve this operation with the ADC, I am transmitting data to the ADC in this order:

1) Setting the ADC input range to 0V to 10.24V in the program registers of the ADC: 0x0B05, 0x0D05, 0x0F05, 0x1105.

2) Sending the following commands for obtaining conversions: 0xC0, 0xC4, 0xC8, 0xCC. Then, repeating this cycle starting at 0xC0.

My software is based on the spi_controller_repeated_fifo_dma_interrupts example from the SDK. In this example, an SPI frame is sent every 1 second using Timer0.  I modified the .syscfg so the SPI clock is 1MHz, clock polarity is low, and clock phase is high to capture data on the falling edge.

I have been using a lab oscilloscope to observe the SPI waveforms. As the SPI frames are sent every 1 second, I can observe the operation of the system with the naked eye. The oscilloscope is triggered on the falling edge of the chip select.

In order to observe the desired system behavior on the oscilloscope, I have written the following code. I have highlighted the code I added throughout to achieve this behavior. I did not observe 0x00 being transmit over the PICO line on the oscilloscope. Without this block of code, I observed the following messages being transmit over PICO on the oscilloscope: 0x0B05, 0x0F05, 0xC0, CxC8, 0xC0, 0xC8... Essentially, every other message to the ADC was skipped. This does not align with my current understanding of the MSPM0G3507 or ADS8684A, so I am looking for some clarity as to what is going wrong here. 

#include "ti/driverlib/dl_spi.h"
#include "ti_msp_dl_config.h"

/*
 * Number of bytes for SPI packet size.
 * The packet will be transmitted by the SPI controller.
 * This example uses FIFOs, and the maximum FIFO size is 4.
 */
#define SPI_Packet_Size (4)

#define CHANNEL0_RANGE_SELECT_ADDRESS 0x05
#define CHANNEL1_RANGE_SELECT_ADDRESS 0x06
#define CHANNEL2_RANGE_SELECT_ADDRESS 0x07
#define CHANNEL3_RANGE_SELECT_ADDRESS 0x08

#define SENSOR_RANGE_10V_BI     0x00
#define SENSOR_RANGE_10V_UNI    0x05
#define SENSOR_RANGE_5V_BI      0x01
#define SENSOR_RANGE_5V_UNI     0x06
#define SENSOR_RANGE_2V5_BI     0x02
#define SENSOR_RANGE_20mA_BI    0x03
#define SENSOR_RANGE_20mA_UNI   0x0F

#define ADC_COMMAND_CONT_OP         0x00
#define ADC_COMMAND_MAN_CHANNEL0    0xC0
#define ADC_COMMAND_MAN_CHANNEL1    0xC4
#define ADC_COMMAND_MAN_CHANNEL2    0xC8
#define ADC_COMMAND_MAN_CHANNEL3    0xCC

/* Data for SPI to transmit */
uint8_t gTxPacket[SPI_Packet_Size];

/* Data for SPI to receive */
uint8_t gRxPacket[SPI_Packet_Size];

/* Array to set channel input range of ADS8684A for Channels 0 to 3*/
uint8_t SensorRange[] = {SENSOR_RANGE_20mA_UNI, SENSOR_RANGE_20mA_UNI, SENSOR_RANGE_20mA_UNI, SENSOR_RANGE_20mA_UNI};

/* LSB count output of ADC conversion */
uint16_t ChannelStep;

/* Output of ADC conversion in Volts */
float ChannelValue[4];

/* Value in Volts of LSB for given input range */
float LSB;


volatile bool isSPIDataTransmitted, isDMATXDataTransferred, isDMARXDataTransferred, isTransmitReady;

void SPI_send(void);



int main(void)
{
    isTransmitReady        = false;
    isSPIDataTransmitted   = false;
    isDMATXDataTransferred = false;
    isDMARXDataTransferred = false;

    SYSCFG_DL_init();

    NVIC_EnableIRQ(SPI_0_INST_INT_IRQN);
    NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);

    /* Program registers of ADC */
    for(int j = 0; j < 4; j++){
        gTxPacket[0] = 0x00;
        gTxPacket[1] = 0x00;
        gTxPacket[2] = 0x00;
        if (isTransmitReady == true) {
            isTransmitReady = false;
            SPI_send();
        } else {
            __WFI();
        }

        uint8_t nRW = 1;
        gTxPacket[0] = ((CHANNEL0_RANGE_SELECT_ADDRESS + j) << 1) + nRW;
        gTxPacket[1] = SensorRange[j];
        if (isTransmitReady == true) {
            isTransmitReady = false;
            SPI_send();
        } else {
            __WFI();
        }
    }
   
    gTxPacket[1] = 0x00;
    gTxPacket[0] = 0x00;
    if (isTransmitReady == true) {
        isTransmitReady = false;
        SPI_send();
    } else {
        __WFI();
    }
   
    /* Begin with command for manual conversion of Channel 0 */
    gTxPacket[0] = ADC_COMMAND_MAN_CHANNEL0;
    gTxPacket[1] = 0x00;
    if (isTransmitReady == true) {
        isTransmitReady = false;
        SPI_send();
    } else {
        __WFI();
    }

    gTxPacket[1] = 0x00;
    gTxPacket[0] = 0x00;
    if (isTransmitReady == true) {
        isTransmitReady = false;
        SPI_send();
    } else {
        __WFI();
    }
   
    while (1) {
        for(int i = 0; i < 4; i++){
            /* Modifying TxPacket to send command for conversion of the next channel*/
            if(i == 3){
                gTxPacket[0] = ADC_COMMAND_MAN_CHANNEL0;
            } else {
                gTxPacket[0] = ADC_COMMAND_MAN_CHANNEL0 + 4*(i + 1);
            }
            if (isTransmitReady == true) {
                isTransmitReady = false;
                SPI_send();
                ChannelStep = (gRxPacket[2] << 8) | gRxPacket[3];
            } else {
                __WFI();
            }
            gTxPacket[1] = 0x00;
            gTxPacket[0] = 0x00;
            if (isTransmitReady == true) {
                isTransmitReady = false;
                SPI_send();
            } else {
                __WFI();
            }

            switch(SensorRange[i]){
                    case SENSOR_RANGE_10V_BI:
                    LSB = 0.0003125;
                    ChannelValue[i] = (float)ChannelStep*LSB - 10.24;
                    break;
                case SENSOR_RANGE_10V_UNI:
                    LSB = 0.00015625;
                    ChannelValue[i] = (float)ChannelStep*LSB;
                    break;
                case SENSOR_RANGE_5V_BI:
                    LSB = 0.00015625;
                    ChannelValue[i] = (float)ChannelStep*LSB - 5.12;
                    break;
                case SENSOR_RANGE_5V_UNI:
                    LSB = 0.000078125;
                    ChannelValue[i] = (float)ChannelStep*LSB;      
                    break;          
                case SENSOR_RANGE_2V5_BI:
                    LSB = 0.000078125;
                    ChannelValue[i] = (float)ChannelStep*LSB - 2.56;
                    break;
                case SENSOR_RANGE_20mA_UNI:
                    LSB = 0.00001953125;
                    ChannelValue[i] = (float)ChannelStep*LSB;                    
                    break;
                case SENSOR_RANGE_20mA_BI:
                    LSB = 0.0000390625;
                    ChannelValue[i] = (float)ChannelStep*LSB - 1.28;
                    break;                    
            }
        }
    }
}


void SPI_send(void)
{
    /*
     * Configure DMA source, destination and size to transfer data from
     * gTxPacket to TXDATA. The DMA transfer will start when TX interrupt is
     * set, which it should already be set (which indicates that the SPI is
     * ready to transfer) so the DMA will begin this transfer when the channel
     * is enabled.
     */

    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTxPacket[0]);
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)(&SPI_0_INST->TXDATA));
    DL_DMA_setTransferSize(
        DMA, DMA_CH0_CHAN_ID, sizeof(gTxPacket) / sizeof(gTxPacket[0]));

    /*
     * Configure DMA source, destination and size from RXDATA to gRxPacket.
     * The DMA transfer will start when the RX interrupt is set, which happens
     * when the device receives data.
     */
    DL_DMA_setSrcAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t)(&SPI_0_INST->RXDATA));
    DL_DMA_setDestAddr(DMA, DMA_CH1_CHAN_ID, (uint32_t) &gRxPacket[0]);
    DL_DMA_setTransferSize(DMA, DMA_CH1_CHAN_ID, SPI_Packet_Size);

    /*
     * The SPI TX interrupt is already set, indicating the SPI is ready to
     * transmit data, so enabling the DMA will start the transfer
     */
    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
    DL_DMA_enableChannel(DMA, DMA_CH1_CHAN_ID);
    /*
     * Wait in SLEEP mode until SPI_Packet_Size bytes have been transferred
     * from gTxPacket to the SPI TXFIFO, and the DMA interrupt is triggered
     */
    while (false == isDMATXDataTransferred) {
        __WFE();
    }

    /*
     * Wait until the SPI has transmitted all the data and the TXFIFO
     * is empty
     */
    while (false == isSPIDataTransmitted) {
        __WFE();
    }

    /*
     * Wait in SLEEP mode until SPI_Packet_Size bytes have been transferred
     * from SPI TXFIFO to gRxPacket, and the DMA interrupt is triggered
     */
    while (false == isDMARXDataTransferred) {
        __WFE();
    }

    isSPIDataTransmitted   = false;
    isDMATXDataTransferred = false;
    isDMARXDataTransferred = false;

   
}

void SPI_0_INST_IRQHandler(void)
{
    switch (DL_SPI_getPendingInterrupt(SPI_0_INST)) {
        case DL_SPI_IIDX_DMA_DONE_TX:
            /* DMA is done transferring data from gTxPacket to TXFIFO */
            isDMATXDataTransferred = true;
            break;
        case DL_SPI_IIDX_TX_EMPTY:
            /* SPI is done transmitting data and TXFIFO is empty */
            isSPIDataTransmitted = true;
            break;
        case DL_SPI_IIDX_DMA_DONE_RX:
            /* DMA is done transferring data from RXFIFO to gRxPacket*/
            isDMARXDataTransferred = true;
        default:
            break;
    }
}

void TIMER_0_INST_IRQHandler(void)
{
    switch (DL_Timer_getPendingInterrupt(TIMER_0_INST)) {
        case DL_TIMER_IIDX_ZERO:
            isTransmitReady = true;
            DL_GPIO_togglePins(GPIO_LEDS_PORT,
                GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN);
            break;
        default:
            break;
    }
}
  • Hi, thanks for the question! Let me replicate this using your code above and see if I can get you a solution to this issue.

    -Brian

  • Okay. I also have an edit on my above post. "Without this block of code, I observed the following messages being transmit over PICO on the oscilloscope: 0x0B05, 0x0F05, 0xC0, CxC8, 0xC0, 0xC8..." I observe 0x0D05, 0x1105, 0xC4, 0xCC, 0xC4, 0xCC... and on and on. The odd messages (first, third, fifth...) are not transmitted/observed on the oscilloscope.

  • Hey sorry for the delay here, can you provide a screenshot of the logic output? Also what do you have your frame size set to? I added your code into Theia and ran it and it looks like I am getting 3 transactions of 0x00, then 0xC0, 3 more 0x00, and so on in a repeating pattern. Looks like I am getting the expected results, can you also share your sysconfig settings?

  • That's okay, I figured out what I was doing wrong. It had more to do with my own software than anything, especially placement of the __WFI() function. I rearranged some things and achieved the behavior I desired. Right now, my project is in a testing phase, and this is enough to validate the SPI connection between the ADC and MCU. 

    I can provide more information if needed. 

    I appreciate you looking into this.

  • Hey AudB, 

    Nice to hear that!

    Please, could you share your code that is working. I'm strugling to do something similar, in my case I'll write/read from/to a FRAM.

    Thanks,

    Andrea

  • I should probably let AudB speak, but just in case: I suspect the trouble had to do with the highlighted code blocks, each of which (accidentally) says "Either wait for the timer Or do the transaction", rather than "wait for the timer, then do the transaction". You probably wouldn't use this part of the code for talking to a serial FRAM.

    The SPI_send() logic looks fine; there are some refinements possible for the longer transactions you would probably have with a storage device.