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.

RTOS/MSP432E411Y: Documentation of TI Drivers (SimpleLink)

Part Number: MSP432E411Y
Other Parts Discussed in Thread: MSP-EXP432E401Y

Tool/software: TI-RTOS

Dear TI-Experts,

I want to learn more about TI-RTOS drivers in the SimpleLink software package. As far as I understand those are called TI-Drivers (and support also FreeRTOS and NoRTOS). I could only find lots of examples (thanks for those) and doxygen documentation. I'm missing a user guide. I got the impression, that SimpleLink Academy should fill the gap, but I couldn't find anything related to ADC usage.

Could you send me helpful links?

For the driverlib I'm also missing a user guide like "TivaWare™ Peripheral Driver Library User Guide" SPMU298D.

Could you send me helpful links?

Are driverlib for MSP432E411Y and TM4C129 similar enough to use SPMU298D instead?

  • Hello Sven,

    Sven Probst said:
    I want to learn more about TI-RTOS drivers in the SimpleLink software package.

    The Documentation Overview Page and the SimpleLink Academy are the best starting points to understand TI-Drivers.

    Sven Probst said:
    As far as I understand those are called TI-Drivers (and support also FreeRTOS and NoRTOS)

    Your understanding is right.

    Sven Probst said:
    I'm missing a user guide. I got the impression, that SimpleLink Academy should fill the gap, but I couldn't find anything related to ADC usage.

    Did you already look at the SimpleLink Academy module called "Project 0" under TI-Drivers? It introduces the concepts of TI-Drivers using ADC TI-Driver.

    The Documentation Overview Page will lead you to the "TI Drivers Runtime APIs (doxygen)" page. If you click on ADC.h file, the top section has an overview on the operation of the driver along with implementation specifics.

    We don't have any User's Guide for the TI-Drivers.

    Can you list a few things that you think are missing from the above mentioned documentation? I will provide this feedback to our internal teams so that we can improve the documentation.

    Sven Probst said:
    For the driverlib I'm also missing a user guide like "TivaWare™ Peripheral Driver Library User Guide" SPMU298D.

    Thanks for pointing that out. I will file a bug so that we can add it in the future releases.

    Sven Probst said:
    Are driverlib for MSP432E411Y and TM4C129 similar enough to use SPMU298D instead?

    Yes they are similar. We would recommend using the TI-Drivers as they are RTOS aware, portable etc. Use driverlib for fine tuning the TI-Driver or if you think the TI-Drivers won't work for your application.

    If you do decide to use driverlib API, we would really appreciate if you can provide feedback as to why you don't want to use TI-Drivers.

    Hope this information helps!

    Thanks,

    Sai

  • Hello Sai,

    thank you very much for the hints / links. I actually missed the project0 chapter about the ADC.
    I read it now, but not the code it referes to. It seems to be very simple. Unfortunately I have to use the sequencer and hardware averaging of the ADC, which is not covered in the example. I'll open a new post as soon as I have worked on project0 and questions arise.
    I assume that documentation about ADCBuf.h and ADCBufMSP432E4.h will help with sequencer and hardware averaging?

    The "TI Drivers Runtime APIs (doxygen)" page is useful during the implemetation phase of the software as a reference guide. But it is too fragmented to get an easy understanding of e.g. the ADC driver's concepts and its features or limitations. Maybe a SimpleLink Academy sesssion about each µC peripheral could get added in the future (maybe based on the "TI Drivers" examples).

    I.M.H.O. there is a too large gap between the SimpleLink Academy project0 (which is something like a "getting started guide") and the API documentation (reference guide) and this gap needs to get filled somehow (e.g. by a user's guide).

    In the Tiva-world SPMU298D helps somewhat in understanding concepts of the TI-RTOS drivers(, albeit I miss a TI-RTOS driver user's guide for Tiva also. [off topic]) For that reason I was asking about a driverlib user guide. I'm not going to use the driverlib directly.

    The c code examples, e.g. adcbufmultisequencer_MSP_EXP432E401Y_tirtos_ccs, cannot fill the gap, too, because there are very few comments and the splitting between board file and application files makes understanding even more difficult.

    In the past I felt very comfortable with a concept from ATMEL: They provided a getting started guide (based on doxygen) for every processor hardware driver. It contained a conceptual overview, simple to complex example code, and each section and line of the code got an explanation.
    See for example asf.atmel.com/.../adc_quickstart.html
    Due to the limited scope of each guide it covered many details. The set of all those guides I would call the driver package's user's guide.
  • Hello Sven,

    I apologize for the delay in getting back.

    Documentation of ADCBufMSP432E4.h will help with the sequencer. Let me know if the following explanation is useful.

    To initialize the sequencers, in the board file "MSP_EXP432E401Y.c"
    * Specify the sequencer to use by setting ".adcSequence" in the array "adcBuf0MSP432E4Channels" for each channel/sequencer combination needed.
    * Change the sequencer priority for each sequencer to be used in the array "seqPriorities".

    In the application,
    * Create and initialize "ADCBuf_Conversion" object for each sequencer.
    * To make each sequencer sample multiple channels, create the conversion object as an array, example ADCBuf_Conversion continuousConversion[2] and initialize each channel within the conversion object.

    For conversion,
    * Call ADCBuf_convert for each conversion object.
    * If using multiple channels, specify the channel count when calling ADCBuf_convert. Example ADCBuf_convert(adcbuf, conversion, 3).

    Thanks,
    Sai
  • Hello Sai,

    thank you very much for your explanation.

    For convenience of other forum users I added two files containing definitions for 20 ADCs of the MSP-EXP432E401Y. It is using 2 sequencers of ADC 0 and one of ADC 1. The ADC entries in "MSP_EXP432E401Y.c" have to get removed.

    Following code fragment shows usage:

    #include "Board2.h"
    ...

    /* structure to hold argument to callback function */
    typedef struct adcArgStruct
    {
        uint8_t id;
    } adcArgStruct_t;
    
    typedef adcArgStruct_t adcArgStructArray_t[3];
    
    /* a result is the average of SAMPLES_PER_CHANNEL (already hardware averaged) samples */
    #define RESULT_SAMPLERATE           10  // Hz
    
    /* Identifier used in callback function to distinguish between sequencers */
    #define ADCBUF0_SQ0_ID              3
    #define ADCBUF0_SQ1_ID              5
    #define ADCBUF1_SQ0_ID              7
    
    /* channels used in each sequencer */
    #define ADCBUF0_SQ0_USED_CHANNELS   8
    #define ADCBUF0_SQ1_USED_CHANNELS   4
    #define ADCBUF1_SQ0_USED_CHANNELS   8
    #define TOTAL_USED_CHANNELS         (ADCBUF0_SQ0_USED_CHANNELS+ADCBUF0_SQ1_USED_CHANNELS+ADCBUF1_SQ0_USED_CHANNELS)
    
    /* number of samples to calculate software average on (sample rate as specified in call of ADCBuf_open)
     *  - we use additional hardware averaging by ADCs at ADC conversion rate (1MS/s) */
    #define SAMPLES_PER_CHANNEL     12
    #define ADCBUF0_SQ0_BUFFERSIZE  (ADCBUF0_SQ0_USED_CHANNELS*SAMPLES_PER_CHANNEL)
    #define ADCBUF0_SQ1_BUFFERSIZE  (ADCBUF0_SQ1_USED_CHANNELS*SAMPLES_PER_CHANNEL)
    #define ADCBUF1_SQ0_BUFFERSIZE  (ADCBUF1_SQ0_USED_CHANNELS*SAMPLES_PER_CHANNEL)
    
    /* define buffers for continuous conversion mode of driver (double buffer approach via DMA) */
    uint16_t bufferAdc0Sq0Ping[ADCBUF0_SQ0_BUFFERSIZE];
    uint16_t bufferAdc0Sq0Pong[ADCBUF0_SQ0_BUFFERSIZE];
    uint16_t bufferAdc0Sq1Ping[ADCBUF0_SQ1_BUFFERSIZE];
    uint16_t bufferAdc0Sq1Pong[ADCBUF0_SQ1_BUFFERSIZE];
    uint16_t bufferAdc1Sq0Ping[ADCBUF1_SQ0_BUFFERSIZE];
    uint16_t bufferAdc1Sq0Pong[ADCBUF1_SQ0_BUFFERSIZE];
    
    /* temporal buffer for channel separation in callback function */
    int32_t tempBuffer[SAMPLES_PER_CHANNEL];
    /* array to hold averaged results of all channels (ADC0 Sq0, ADC0 Sq1, ADC1 Sq0) */
    uint16_t ADC_uC_result[TOTAL_USED_CHANNELS];
    /* array to hold arguments to callback functions */
    adcArgStructArray_t args;
    
    /*
     * Callback function of ADC 0. This function is called whenever a buffer is full.
     * All ADC-DMA interrupts (2 per ADC) should have same priority,
     * thus one callback function for both ADCs should be sufficient (no preemption)?
     */
    void adcBuf0Callback(ADCBuf_Handle handle, ADCBuf_Conversion *conversion,
        void *completedADCBuffer, uint32_t completedChannel) {
        uint_fast16_t channelIdx, sampleReadIdx, sampleWriteIdx;
        uint16_t *completedBuffer = (uint16_t *) completedADCBuffer;
        adcArgStruct_t* argument;
        uint8_t id;
    
        argument = (adcArgStruct_t*)(conversion->arg);
        id = argument->id;
        if (id == ADCBUF0_SQ0_ID)
        {
            for (channelIdx = 0; channelIdx < ADCBUF0_SQ0_USED_CHANNELS; channelIdx++) {
                /* data order: ch0s0, ch1s0, ..., chNs0, ch0s1, ch1s1, ..., chNs1, ... */
                sampleReadIdx = channelIdx;
                /* copy all samples of one channel into one buffer */
                for (sampleWriteIdx = 0; sampleWriteIdx<SAMPLES_PER_CHANNEL; sampleWriteIdx++) {
                    tempBuffer[sampleWriteIdx] =  (int32_t)completedBuffer[sampleReadIdx];
                    sampleReadIdx += ADCBUF0_SQ0_USED_CHANNELS;
                }
                /* perform sample processing (averaging) */
                ADC_uC_result[channelIdx] = (uint16_t)average(tempBuffer, SAMPLES_PER_CHANNEL);
            }
        }
        else
        {
            for (channelIdx = 0; channelIdx < ADCBUF0_SQ1_BUFFERSIZE; channelIdx++) {
                // ...
            }
        }
    }
    
    /* callback function of ADC 1
     * all ADC-DMA interrupts (2 per ADC) should have same priority,
     * thus one callback function for both ADCs should be sufficient (no preemption)?
     */
    
    void adcBuf1Callback(ADCBuf_Handle handle, ADCBuf_Conversion *conversion,
        void *completedADCBuffer, uint32_t completedChannel) {
       // ...
    }
    
    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        ADCBuf_Handle adcBuf0,adcBuf1;
        ADCBuf_Params adcBufParams;
        ADCBuf_Conversion conversionParamADCbuf0Sq0[ADCBUF0_SQ0_USED_CHANNELS];
        ADCBuf_Conversion conversionParamADCbuf0Sq1[ADCBUF0_SQ1_USED_CHANNELS];
        ADCBuf_Conversion conversionParamADCbuf1Sq0[ADCBUF1_SQ0_USED_CHANNELS];
    
        /* Call driver init functions */
        ADCBuf_init();
    
        /* Set up an ADCBuf peripheral in ADCBuf_RECURRENCE_MODE_CONTINUOUS */
        ADCBuf_Params_init(&adcBufParams);
        adcBufParams.callbackFxn = adcBuf0Callback;
        adcBufParams.recurrenceMode = ADCBuf_RECURRENCE_MODE_CONTINUOUS;
        adcBufParams.returnMode = ADCBuf_RETURN_MODE_CALLBACK;
        adcBufParams.samplingFrequency = RESULT_SAMPLERATE*SAMPLES_PER_CHANNEL;
        adcBuf0 = ADCBuf_open(Board_ADCBUF0, &adcBufParams);
    
        /* same parameters but different callback function for ADC 1 */
        adcBufParams.callbackFxn = adcBuf1Callback;
        adcBuf1 = ADCBuf_open(Board_ADCBUF1, &adcBufParams);
    
        /* define ADC clock as 16MHz -> conversion rate = 1MSps at fPLL = 480MHz; ADCs share same clock, thus setting ADC 0 only */
        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, (480/16));
        /* enable hardware averaging of 32 consecutive ADC samples taken at conversion rate */
        ADCHardwareOversampleConfigure(ADC0_BASE,32);
        ADCHardwareOversampleConfigure(ADC1_BASE,32);
    
        /* set identifier values in array of arguments for callback functions */
        args[0].id = ADCBUF0_SQ0_ID;
        args[1].id = ADCBUF0_SQ1_ID;
        args[2].id = ADCBUF1_SQ0_ID;
    
        /* Configure the conversion struct for all channels on ADCBuf0 sequencer 0 */
        uint8_t chIdx;
        for (chIdx = 0; chIdx<ADCBUF0_SQ0_USED_CHANNELS; chIdx++)
        {
            conversionParamADCbuf0Sq0[chIdx].arg = &args[0];
            conversionParamADCbuf0Sq0[chIdx].sampleBuffer = NULL;
            conversionParamADCbuf0Sq0[chIdx].sampleBufferTwo = NULL;
            conversionParamADCbuf0Sq0[chIdx].samplesRequestedCount = ADCBUF0_SQ0_BUFFERSIZE;
        }
        /* first channel entry takes buffer pointer */
        conversionParamADCbuf0Sq0[0].sampleBuffer = bufferAdc0Sq0Ping;
        conversionParamADCbuf0Sq0[0].sampleBufferTwo = bufferAdc0Sq0Pong;
    
        conversionParamADCbuf0Sq0[0].adcChannel = Board_ADCBUF0CHANNEL0;
        conversionParamADCbuf0Sq0[1].adcChannel = Board_ADCBUF0CHANNEL1;
        conversionParamADCbuf0Sq0[2].adcChannel = Board_ADCBUF0CHANNEL2;
        conversionParamADCbuf0Sq0[3].adcChannel = Board_ADCBUF0CHANNEL3;
        conversionParamADCbuf0Sq0[4].adcChannel = Board_ADCBUF0CHANNEL4;
        conversionParamADCbuf0Sq0[5].adcChannel = Board_ADCBUF0CHANNEL5;
        conversionParamADCbuf0Sq0[6].adcChannel = Board_ADCBUF0CHANNEL6;
        conversionParamADCbuf0Sq0[7].adcChannel = Board_ADCBUF0CHANNEL7;
    
    
        /* Configure the conversion struct for all channels on ADCBuf0 sequencer 1 */
        for (chIdx = 0; chIdx<ADCBUF0_SQ1_USED_CHANNELS; chIdx++)
        {
            conversionParamADCbuf0Sq1[chIdx].arg = &args[1];
            conversionParamADCbuf0Sq1[chIdx].sampleBuffer = NULL;
            conversionParamADCbuf0Sq1[chIdx].sampleBufferTwo = NULL;
            conversionParamADCbuf0Sq1[chIdx].samplesRequestedCount = ADCBUF0_SQ1_BUFFERSIZE;
        }
        /* first channel entry takes buffer pointer */
        conversionParamADCbuf0Sq1[0].sampleBuffer = bufferAdc0Sq1Ping;
        conversionParamADCbuf0Sq1[0].sampleBufferTwo = bufferAdc0Sq1Pong;
    
        conversionParamADCbuf0Sq1[0].adcChannel = Board_ADCBUF0CHANNEL8;
        conversionParamADCbuf0Sq1[1].adcChannel = Board_ADCBUF0CHANNEL9;
        conversionParamADCbuf0Sq1[2].adcChannel = Board_ADCBUF0CHANNEL10;
        conversionParamADCbuf0Sq1[3].adcChannel = Board_ADCBUF0CHANNEL11;
    
    
        /* Configure the conversion struct for all channels on ADCBuf1 sequencer 0 */
        for (chIdx = 0; chIdx<ADCBUF1_SQ0_USED_CHANNELS; chIdx++)
        {
            conversionParamADCbuf1Sq0[chIdx].arg = &args[2];
            conversionParamADCbuf1Sq0[chIdx].sampleBuffer = NULL;
            conversionParamADCbuf1Sq0[chIdx].sampleBufferTwo = NULL;
            conversionParamADCbuf1Sq0[chIdx].samplesRequestedCount = ADCBUF1_SQ0_BUFFERSIZE;
        }
        /* first channel entry takes buffer pointer */
        conversionParamADCbuf1Sq0[0].sampleBuffer = bufferAdc1Sq0Ping;
        conversionParamADCbuf1Sq0[0].sampleBufferTwo = bufferAdc1Sq0Pong;
    
        conversionParamADCbuf1Sq0[0].adcChannel = Board_ADCBUF1CHANNEL0;
        conversionParamADCbuf1Sq0[1].adcChannel = Board_ADCBUF1CHANNEL1;
        conversionParamADCbuf1Sq0[2].adcChannel = Board_ADCBUF1CHANNEL2;
        conversionParamADCbuf1Sq0[3].adcChannel = Board_ADCBUF1CHANNEL3;
        conversionParamADCbuf1Sq0[4].adcChannel = Board_ADCBUF1CHANNEL4;
        conversionParamADCbuf1Sq0[5].adcChannel = Board_ADCBUF1CHANNEL5;
        conversionParamADCbuf1Sq0[6].adcChannel = Board_ADCBUF1CHANNEL6;
        conversionParamADCbuf1Sq0[7].adcChannel = Board_ADCBUF1CHANNEL7;
    
        /* check ADCs to open correctly */
        if (!adcBuf0){
            /* AdcBuf did not open correctly. */
            while(1);
        }
    
        if (!adcBuf1){
            /* AdcBuf did not open correctly. */
            while(1);
        }
    
        /* Start converting ADC 0 sequencer 0. */
        if (ADCBuf_convert(adcBuf0, conversionParamADCbuf0Sq0, ADCBUF0_SQ0_USED_CHANNELS) !=
            ADCBuf_STATUS_SUCCESS) {
            /* Did not start conversion process correctly. */
            while(1);
        }
    
        /* Start converting ADC 0 sequencer 1. */
        if (ADCBuf_convert(adcBuf0, conversionParamADCbuf0Sq1, ADCBUF0_SQ1_USED_CHANNELS) !=
            ADCBuf_STATUS_SUCCESS) {
            /* Did not start conversion process correctly. */
            while(1);
        }
    
        /* Start converting ADC 1 sequencer 0. */
        if (ADCBuf_convert(adcBuf1, conversionParamADCbuf1Sq0, ADCBUF1_SQ0_USED_CHANNELS) !=
            ADCBuf_STATUS_SUCCESS) {
            /* Did not start conversion process correctly. */
            while(1);
        }
    
        /*
         * Go to sleep in the foreground thread forever. The data will be collected
         * by callback functions
         */
        while(1) {
            sleep(1000);
        }
    
    }

    Board2.zip

**Attention** This is a public forum