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.

EK-TM4C123GXL: ADC on TI-RTOS

Part Number: EK-TM4C123GXL
Hi everyone,
as you can guess from the title, I'm trying to implement the operation of the ADC in the TI-RTOS, since there are no libraries of this peripheral for TIVA devices.
FYI, I am using CCS v12.1.0 with the TM4C123GXL eval kit.

I have already downloaded the TIRTOS for TivaC v2.16.00.08 and I am able to use all the functionalities correctly. I saw in other post how to add the correct version of the xdc_tool.
I also use correctly GPIO, UART, Timers etc.

My objective is to enable the interrupt from the ADC sequencer0, generating an hwi and then store the data in a global variable.

When I saw that ADC lib is missing, the first try was to use a timer, generating a hwi and, usind the TIVA driverlib API, I was able to read the value correctly. Then, I wanna try to create the "TIRTOS for Tiva" part of the lib by myself, to use ADC API with a custom board in the future.

Now, I have already seen that in the forum there are posts related to ADC but some questions are unresolved or answers aren't exaustive and almost all posts are for MSP or CC microcontrollers 
that have already the implemantation of ADC.
I start from the "empty" example for tirtos.
Firstly I've added in the "EK-TM4C123GXL.c" file the following:

In the "EK-TM4C123GXL.h" file I add the following:

/*!
* @def EK_TM4C123GXL_ADCName
* @brief Enum of ADCs on the EK_TM4C123GXL dev board
*/
typedef enum EK_TM4C123GXL_ADCName {
EK_TM4C123GXL_ADC0 = 0,

EK_TM4C123GXL_ADCCOUNT
} EK_TM4C123GXL_ADCName;

extern void EK_TM4C123GXL_initADC(void);


I've tryed to create by myself ADC.h and ADC.c files, observing ADC source and header files found online for
MSP or CC microcontrollers, i attach the files below.

myADC.h

myADC.c
/*
 * ADC.c
 *
 *  Created on: 10 feb 2023
 *      Author: marika
 */

/*
 * Copyright (c) 2016-2019, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 *  ======== ADC.c ========
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#include <myADC.h>
#include <myADCTiva.h>
#include <driverlib/adc.h>


/* Externs */
extern const ADC_Config ADC_config[];

/* Used to check status and initialization */
static int ADC_count= -1;


/* Default ADC parameters structure */
const ADC_Params ADC_defaultParams = {//TO CHECK
      (uintptr_t) NULL    /* custom */
};



/*
 *  ======== ADC_close ========
 */
void ADC_close(ADC_Handle handle)
{
    handle->fxnTablePtr->closeFxn(handle);
}

/*
 *  ======== ADC_control ========
 */
int ADC_control(ADC_Handle handle, unsigned int cmd, void *arg)
{
    return (handle->fxnTablePtr->controlFxn(handle, cmd, arg));
}


/*
 *  ======== ADC_init ========
 */
void ADC_init(void)
{
   if (ADC_count == -1) {
            /* Call each driver's init function */
            for (ADC_count = 0; ADC_config[ADC_count].fxnTablePtr != NULL; ADC_count++) {
                ADC_config[ADC_count].fxnTablePtr->initFxn((ADC_Handle)&(ADC_config[ADC_count]));
            }
        }
}

/*
 *  ======== ADC_open ========
 */
ADC_Handle ADC_open(unsigned int index, ADC_Params *params)
{
       ADC_Handle         handle;

        if (index >= ADC_count) {
            return (NULL);
        }

        /* If params are NULL use defaults */
        if (params == NULL) {
            params = (ADC_Params *) &ADC_defaultParams;
        }

        /* Get handle for this driver instance */
        handle = (ADC_Handle)&(ADC_config[index]);

        return (handle->fxnTablePtr->openFxn(handle, params));

}

/*
 *  ======== ADC_Params_init ========
 */
void ADC_Params_init(ADC_Params *params)
{
    *params = ADC_defaultParams;
}





At the same way, I created additionally 2 files (myADCTiva.h/c) as attached below.
myADCTiva.h

myADCTiva.c
/*
 * myADCTiva.c
 *
 *  Created on: 14 feb 2023
 *      Author: nicol
 */
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Types.h>

#include <myADCTiva.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/family/arm/m3/Hwi.h>

#include <inc/hw_memmap.h>
#include <inc/hw_ints.h>
#include <inc/hw_types.h>
#include <driverlib/adc.h>
#include <inc/hw_sysctl.h>
#include <driverlib/sysctl.h>

/* ADCTiva functions */
void ADCTiva_close(ADC_Handle handle);
int ADCTiva_control(ADC_Handle handle, unsigned int cmd, void *arg);
void ADCTiva_init(ADC_Handle handle);
ADC_Handle ADCTiva_open(ADC_Handle handle, ADC_Params *params);

/* ADC function table for ADCTiva implementation */
const ADC_FxnTable ADCTiva_fxnTable = {
    ADCTiva_close,
    ADCTiva_control,
    ADCTiva_init,
    ADCTiva_open

};

/*
 *  ======== ADCTiva_close ========
 */
void ADCTiva_close(ADC_Handle handle){

    ADCTiva_Object             *object = handle->object;
    ADCTiva_HWAttrs const      *hwAttrs = handle->hwAttrs;

    /* Disable ADC and interrupts. */
    ADCIntDisable(hwAttrs->baseAddr, 0);
    ADCSequenceDisable(hwAttrs->baseAddr, 0);

    Hwi_destruct(&(object->hwi));
    Semaphore_destruct(&(object->adcSem));

    object->isOpen = false;

    Log_print1(Diags_USER1, "ADC:(%p) closed", hwAttrs->baseAddr);


}

/*
 *  ======== ADCCTiva_control ========
 *  @pre    Function assumes that the handle is not NULL
 */
int ADCTiva_control(ADC_Handle handle, unsigned int cmd, void *arg)
{
    /* No implementation yet */
    return (ADC_STATUS_UNDEFINEDCMD);
}

/*
 *  ======== ADCTiva_hwiFxn ========
 *  Hwi interrupt handler to service the ADC peripheral
 *
 *  The handler is a generic handler for a ADC object.
 */
static void ADCTiva_hwiFxn(UArg arg){

    /* Get the pointer to the object and hwAttrs */
        ADCTiva_Object         *object = ((ADC_Handle)arg)->object;
        ADCTiva_HWAttrs const  *hwAttrs = ((ADC_Handle)arg)->hwAttrs;
//        uint32_t                status;

        /* Clear interrupts */
//        status=ADCIntStatus(hwAttrs->baseAddr, 0,true);
        object->isOpen = false;

        ADCIntClear(hwAttrs->baseAddr, 0);
        ADCSequenceDataGet(ADC0_BASE, 0, &Value);

}

/*
 *  ======== ADCTiva_init ========
 */
void ADCTiva_init(ADC_Handle handle)
{
    /* Mark the object as available */
    ((ADCTiva_Object *) (handle->object))->isOpen = false;
}

/*
 *  ======== ADCTiva_open ========
 */
ADC_Handle ADCTiva_open(ADC_Handle handle, ADC_Params *params)
{
    unsigned int               key;
    ADCTiva_Object             *object = handle->object;
    ADCTiva_HWAttrs const      *hwAttrs = handle->hwAttrs;
    union {
        Semaphore_Params        semParams;
        Hwi_Params              hwiParams;
    } paramsUnion;

    /* Determine if the device index was already opened */
    key = Hwi_disable();
    if(object->isOpen == true){
        Hwi_restore(key);
        Log_warning1("ADC:(%p) already in use.", hwAttrs->baseAddr);
        return (NULL);
    }

    /* Mark the handle as being used */
    object->isOpen = true;
    Hwi_restore(key);

    /* Create Hwi object for this I2C peripheral */
    Hwi_Params_init(&(paramsUnion.hwiParams));
    paramsUnion.hwiParams.arg = (UArg)handle;
    paramsUnion.hwiParams.priority = hwAttrs->intPriority;
    Hwi_construct(&(object->hwi), hwAttrs->intNum, ADCTiva_hwiFxn,
                  &(paramsUnion.hwiParams), NULL);

    /*
     * Create threadsafe handles for this I2C peripheral
     * Semaphore to provide exclusive access to the I2C peripheral
     */
    Semaphore_Params_init(&(paramsUnion.semParams));
    paramsUnion.semParams.mode = Semaphore_Mode_BINARY;
    Semaphore_construct(&(object->adcSem), 1, &(paramsUnion.semParams));

    ADCIntClear(hwAttrs->baseAddr, 0);

    ADCSequenceEnable(hwAttrs->baseAddr, 0);

    ADCIntEnable(hwAttrs->baseAddr, 0);


    /* Return the address of the handle */
    return (handle);

}





So, using the XGCONF window, I was able to create a hwi, setting the interrupt number = 30 (as from the table in the datasheet), and associating the corresponding hwiFxn that I have written in the empty.c. file.


The function in the empty.c is the following:


int32_t read;
uint32_t Value;


Void myADCFnx(UArg arg){
ADCIntClear(ADC0_BASE, 0);
read=ADCSequenceDataGet(ADC0_BASE, 0, &Value);
}

Now, after fix some problems, I finally was able to build the project without errors or warnings.
BUT, I am not able to "generate" the interrupt. This is probably the main goal that I wanna reach.
Once the interrupt was correctly generate/recognized "It's all downhill from here"!!!
Setting a breakpoint on the above function, in debug mode, I'm sure that the function was never called.
Probably I miss something or I am setting something wrong.

Can you help me? xD

Thanks a lot 
Fede

  • Hello Fede,

    I will review this post and provide feedback tomorrow as today is a US holiday for our engineering team.

    Best Regards,

    Ralph Jacobi

  • Hello Fede,

    My initial guess based on your feedback of the ADC interrupt does not trigger and looking at your configuration, I think you may be missing one more interrupt enable.

    There are three levels of interrupt enables in the device:

    1) Master Interrupt enable to allow the NVIC to process interrupts

    2) NVIC specific interrupt enable to indicate to the NVIC a specific peripheral is being configured for interrupts

    3) Peripheral specific interrupt enable to configure the peripheral to use interrupts

    TI-RTOS should always take care of 1) with the default HWI module.

    Your customized ADC file is handling 3) with ADCIntEnable.

    What I don't see is 2), and I am not fully certain how the TI-RTOS kernel is handling that for other peripherals as I don't see the expected API used. Regardless of how the TI-RTOS kernel is handling it for supported peripherals with drivers that have been provided, you can add this to your custom ADC file as well. The trick might be how to best translate it based on your RTOS parameters though as the interrupts in the NVIC are enabled at both a peripheral and sequencer level.

    The API needed is IntEnable(INT_ADC0SS0);

    The possible inputs for this API are as follows:

    #define INT_ADC0SS0             INT_CONCAT(INT_ADC0SS0_, INT_DEVICE_CLASS)
    #define INT_ADC0SS1             INT_CONCAT(INT_ADC0SS1_, INT_DEVICE_CLASS)
    #define INT_ADC0SS2             INT_CONCAT(INT_ADC0SS2_, INT_DEVICE_CLASS)
    #define INT_ADC0SS3             INT_CONCAT(INT_ADC0SS3_, INT_DEVICE_CLASS)
    #define INT_ADC1SS0             INT_CONCAT(INT_ADC1SS0_, INT_DEVICE_CLASS)
    #define INT_ADC1SS1             INT_CONCAT(INT_ADC1SS1_, INT_DEVICE_CLASS)
    #define INT_ADC1SS2             INT_CONCAT(INT_ADC1SS2_, INT_DEVICE_CLASS)
    #define INT_ADC1SS3             INT_CONCAT(INT_ADC1SS3_, INT_DEVICE_CLASS)

    If I am right, adding the correct NVIC enable will allow your interrupts to fire as expected.

    This guess is based on the idea that you correctly added the ADC interrupt to the HWI, I'm not expert enough to say that was done properly off the top of my head but if this doesn't work then that should be reviewed next.

    Best Regards,

    Ralph Jacobi