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.

CCS/TM4C129ENCPDT: ADC interrupt working intermittently

Part Number: TM4C129ENCPDT
Other Parts Discussed in Thread: EK-TM4C1294XL

Tool/software: Code Composer Studio

I'm using the CryptoConnected Launchpad with the TM4C129E, CCS Studio v6.2, driverlib from TivaWare.

I'm trying to use the ADC0, with TIMER0 being responsible for triggering conversion.

On the following code, TIMER1 interrupts every 3 seconds approx. normally, toggling a LED on the board. TIMER0 is configured to trigger the ADC0 on timeout.

However, it is not working properly. Sometimes the interrupt happens, it reaches a breakpoint i've set inside the ISR, and the conversion reading is ok, plus it keeps working until I close CCS or reboot my PC. But most of the times it doesn't work, the program stays on the infinite loop waiting for the interrupt and it never occurs. I've already looked on the register map and everything seems to be ok on the NVIC and on the Timer Regs.

I don't have any idea where the problem may be.

/*
 * ISRs
 */
void ISR_Timer1A() {
	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

	if (led3s == 0) {
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, GPIO_PIN_0);
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_2, GPIO_PIN_2);
		led3s = 1;
	} else {
		led3s = 0;
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, 0);
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_2, 0);
	}

	return;
}

void ISR_ADC0() {
	ADCIntClear(ADC0_BASE, 3);
	while(ADCBusy(ADC0_BASE));
	ADCSequenceDataGet(ADC0_BASE, 3, bufferTemp);

	if (ledConv == 0) {
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, GPIO_PIN_1);
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_3, GPIO_PIN_3);
		ledConv = 1;
	} else {
		ledConv = 0;
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, 0);
		GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_3, 0);
	}

	return;
}

/*
 * Main Loop 
 */

int main(void) {
	led3s = 0;
	ledConv = 0;

	SysCtlClockFreqSet((SYSCTL_USE_OSC | SYSCTL_OSC_INT | SYSCTL_MAIN_OSC_DIS | SYSCTL_SYSDIV_1), 16000000);
	SysCtlPeripheralPowerOn(SYSCTL_PERIPH_GPIOE);
	SysCtlPeripheralPowerOn(SYSCTL_PERIPH_GPION);
	SysCtlPeripheralPowerOn(SYSCTL_PERIPH_TIMER0);
	SysCtlPeripheralPowerOn(SYSCTL_PERIPH_TIMER1);
	SysCtlPeripheralPowerOn(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION));
	GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));
	ADCClockConfigSet(ADC0_BASE, (ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_HALF), 1);
	ADCPhaseDelaySet(ADC0_BASE, ADC_PHASE_0);
	ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
	ADCHardwareOversampleConfigure(ADC0_BASE, 64);
	ADCSequenceDisable(ADC0_BASE, 3);
	ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);
	ADCSequenceStepConfigure(ADC0_BASE, 3, 0, (ADC_CTL_IE | ADC_CTL_END | ADC_CTL_SHOLD_8 | ADC_CTL_CH8));

	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0));
	TimerConfigure(TIMER0_BASE, TIMER_CFG_A_PERIODIC_UP);
	TimerUpdateMode(TIMER0_BASE, TIMER_A, TIMER_UP_LOAD_IMMEDIATE);
	TimerClockSourceSet(TIMER0_BASE, TIMER_CLOCK_SYSTEM);
	TimerLoadSet(TIMER0_BASE, TIMER_A, 1100);
	TimerADCEventSet(TIMER0_BASE, TIMER_ADC_TIMEOUT_A);
	TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER1));
	TimerConfigure(TIMER1_BASE, TIMER_CFG_A_PERIODIC_UP);
	TimerUpdateMode(TIMER1_BASE, TIMER_A, TIMER_UP_LOAD_IMMEDIATE);
	TimerClockSourceSet(TIMER1_BASE, TIMER_CLOCK_SYSTEM);
	TimerLoadSet(TIMER1_BASE, TIMER_A, 48484848);

	ADCIntRegister(ADC0_BASE, 3, *ISR_ADC0);
	ADCIntClear(ADC0_BASE, 3);
	ADCIntEnableEx(ADC0_BASE, ADC_INT_SS3);
	IntEnable(INT_ADC0SS3);
	ADCIntEnable(ADC0_BASE, 3);
	ADCSequenceEnable(ADC0_BASE, 3);

	TimerIntRegister(TIMER1_BASE, TIMER_A, *ISR_Timer1A);
	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

	IntMasterEnable();

	TimerEnable(TIMER0_BASE, TIMER_A);
	TimerEnable(TIMER1_BASE, TIMER_A);

	while (1) {
	}
	return 0;
}

Thanks

  • Sorry for the delayed response.

    So, today I was going to follow Robert's suggestion, add Interrupts to the program. However, when I opened CCS, I swear, I just ran the code that was working WITHOUT any modification, and it wasn't working anymore. I got the initialization message in the terminal, but no conversion values. I am returning to the first problem, ADC working when it feels up to :( what on Earth could be the problem now? I mean, it was working perfectly with several delays and ADC pins, but as it was working Saturday, I just closed CCS. Opened it today and simply ran the code and (almost) nothing works anymore.

  • Hello Luiz,

    Can you please re-share your main.c file so I can load it into the project you sent before and try to run it on my end?
  • here as it is:

    //*****************************************************************************
    //
    // single_ended.c - Example demonstrating how to configure the ADC for
    //                  single ended operation.
    //
    // Copyright (c) 2010-2017 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    //
    //   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.
    //
    // This is part of revision 2.1.4.178 of the Tiva Firmware Development Package.
    //
    //*****************************************************************************
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "driverlib/adc.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    
    //*****************************************************************************
    //
    //! \addtogroup adc_examples_list
    //! <h1>Single Ended ADC (single_ended)</h1>
    //!
    //! This example shows how to setup ADC0 as a single ended input and take a
    //! single sample on AIN0/PE3.
    //!
    //! This example uses the following peripherals and I/O signals.  You must
    //! review these and change as needed for your own board:
    //! - ADC0 peripheral
    //! - GPIO Port E peripheral (for AIN0 pin)
    //! - AIN0 - PE3
    //!
    //! The following UART signals are configured only for displaying console
    //! messages for this example.  These are not required for operation of the
    //! ADC.
    //! - UART0 peripheral
    //! - GPIO Port A peripheral (for UART0 pins)
    //! - UART0RX - PA0
    //! - UART0TX - PA1
    //!
    //! This example uses the following interrupt handlers.  To use this example
    //! in your own application you must add these interrupt handlers to your
    //! vector table.
    //! - None.
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // This function sets up UART0 to be used for a console to display information
    // as the example is running.
    //
    //*****************************************************************************
    void
    InitConsole(void)
    {
        //
        // Enable GPIO port A which is used for UART0 pins.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Configure the pin muxing for UART0 functions on port A0 and A1.
        // This step is not necessary if your part does not support pin muxing.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
    
        //
        // Enable UART0 so that we can configure the clock.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Select the alternate (UART) function for these pins.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, 16000000);
    }
    
    //*****************************************************************************
    //
    // Configure ADC0 for a single-ended input and a single sample.  Once the
    // sample is ready, an interrupt flag will be set.  Using a polling method,
    // the data will be read then displayed on the console via UART0.
    //
    //*****************************************************************************
    int
    main(void)
    {
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
        uint32_t ui32SysClock;
    #endif
    
        //
        // This array is used for storing the data read from the ADC FIFO. It
        // must be as large as the FIFO for the sequencer in use.  This example
        // uses sequence 3 which has a FIFO depth of 1.  If another sequence
        // was used with a deeper FIFO, then the array size must be changed.
        //
        uint32_t pui32ADC0Value[1];
    
        //
        // Set the clocking to run at 20 MHz (200 MHz / 10) using the PLL.  When
        // using the ADC, you must either use the PLL or supply a 16 MHz clock
        // source.
        // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
        // crystal on your board.
        //
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
        ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                           SYSCTL_OSC_MAIN |
                                           SYSCTL_USE_PLL |
                                           SYSCTL_CFG_VCO_480), 20000000);
    #else
        SysCtlClockFreqSet((SYSCTL_OSC_INT | SYSCTL_USE_OSC), 16000000);
    #endif
    
        //
        // Set up the serial console to use for displaying messages.  This is
        // just for this example program and is not needed for ADC operation.
        //
        InitConsole();
    
        //
        // Display the setup on the console.
        //
        UARTprintf("ADC ->\n");
        UARTprintf("  Type: Single Ended\n");
        UARTprintf("  Input Pin: AIN8/PE5\n\n");
    
        //
        // The ADC0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
        //
        // For this example ADC0 is used with AIN8 on port E5.
        // The actual port and pins used may be different on your part, consult
        // the data sheet for more information.  GPIO port E needs to be enabled
        // so these pins can be used.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
        //
        // Select the analog ADC function for these pins.
        // Consult the data sheet to see which functions are allocated per pin.
        // TODO: change this to select the port/pin you are using. -> Changed to Pin 5
        //
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5);
    
        //
        // Enable sample sequence 3 with a processor signal trigger.  Sequence 3
        // will do a single sample when the processor sends a signal to start the
        // conversion.  Each ADC module has 4 programmable sequences, sequence 0
        // to sequence 3.  This example is arbitrarily using sequence 3.
        //
        ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);
    
        //
        // Configure step 0 on sequence 3.  Sample channel 0 (ADC_CTL_CH0) in
        // single-ended mode (default) and configure the interrupt flag
        // (ADC_CTL_IE) to be set when the sample is done.  Tell the ADC logic
        // that this is the last conversion on sequence 3 (ADC_CTL_END).  Sequence
        // 3 has only one programmable step.  Sequence 1 and 2 have 4 steps, and
        // sequence 0 has 8 programmable steps.  Since we are only doing a single
        // conversion using sequence 3 we will only configure step 0.  For more
        // information on the ADC sequences and steps, reference the datasheet.
        //
        ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH8 | ADC_CTL_IE | ADC_CTL_END);
    
        //
        // Since sample sequence 3 is now configured, it must be enabled.
        //
        ADCSequenceEnable(ADC0_BASE, 3);
    
        //
        // Clear the interrupt status flag.  This is done to make sure the
        // interrupt flag is cleared before we sample.
        //
        ADCIntClear(ADC0_BASE, 3);
    
        //
        // Sample AIN0 forever.  Display the value on the console.
        //
        while(1)
        {
            //
            // Trigger the ADC conversion.
            //
            ADCProcessorTrigger(ADC0_BASE, 3);
    
            //
            // Wait for conversion to be completed.
            //
            while(!ADCIntStatus(ADC0_BASE, 3, false))
            {
            }
    
            //
            // Clear the ADC interrupt flag.
            //
            ADCIntClear(ADC0_BASE, 3);
    
            //
            // Read ADC Value.
            //
            ADCSequenceDataGet(ADC0_BASE, 3, pui32ADC0Value);
    
            //
            // Display the AIN8 (PE5) digital value on the console.
            //
            UARTprintf("\nAIN8 = %4d\r", pui32ADC0Value[0]);
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 250ms arbitrarily.
            //
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
            SysCtlDelay(ui32SysClock / 12);
    #else
            SysCtlDelay(33333);
    #endif
        }
    }
    

  • Hello Luiz,

    I suspect the changes you made per my recommendation to the System Clock managed to not get saved correctly as it still looks like the wrong clock settings are used in the file you shared with me.

    I used every single line of code you have supplied in the new file including the new SysCtlDelay of 33333 and had no issue with the example.

    Please look to see if that is the clock settings managed to get reverted back to the non-functional settings.
  • Of course, I've accidentally sent the main fil from another project. Sorry, my bad. 

    The code with PLL clock source works fine. But shouldn't the other one work too, since the board has a PIOSC at 16MHz?

    I also would like to say that using an interrupt trigger to the ADC has worked, using ProcessorTrigger. I will try with a timer.

    Thank you all for the help, I will report soon if my attempts are working

  • Is it not "fair/proper" to ask, "How the MCU (past) appeared to run - and execute - should the System Clock have been "mangled?"    Does not a mistake w/ "System Clock" (most always) confound JTAG?   (causing dreaded, oft occurring Lock-Out!)

    Thus - the verification of "System Clock" as "cure" for poster's, "Inability to achieve an expected Timer Interrupt" [proves) inappropriate.     (this vendor advice IS surely valid - but "in no way" warrants "Verification!")     Poster (still) reports (the continued) "Inability to achieve an expected Timer Interrupt" - does he not?

    Luiz Felipe K Evaristo said:
    I will report soon "if" my attempts are working

    How then can (any) post here be noted as, "Verified?"

  • No, reconfiguring SystemClock solved the issue of the ADC not working, didn't talk about Timer Interrupt yet. However, revising the SystemClock configuration on my first project, now it is working with Timer Interrupt as well.

    I am considering my problem solved, but the question regarding the PIOSC as source clock for the MCU still is up. It seems that the MCU does run with 16MHz clock prom PIOSC, but some peripherals not, such as ADC. Adjusting other things on the project it is possible to run with the MCU at, say, 120MHz and peripherals at 16MHz, as I am aiming to do.
  • I would also like to say thank you kindly, for helping me with these issues (which happened to be quite "dumb" things)
  • Hello Luiz,

    The PIOSC should have no problem to be used for this as long as the ADC gets a 16MHz clock input into it ultimately.

    I tried on my LaunchPad to use these settings you had provided in your file:

    SysCtlClockFreqSet((SYSCTL_OSC_INT | SYSCTL_USE_OSC), 16000000);

    And I was able to get the ADC running with the example you provided that was using Polling method.

    Can you try and use while(!SysCtlPeripheralReady(PERIPH)); after your peripheral enabling? I noticed this is lacking and that could prove very valuable.

    Note that I didn't need to do the while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)); check to get the prior result, but it's still a good practice to incorporate and could have a positive result given the oddness of your situation.

    I also think it would be good to clarify cb1's concern of why if the SysClk change proved to solve the problem, did any code run at all which is a valid concern... was the prior setting to use the code I posted above? If so, then the code would have at least run on the principle that the clocking wouldn't cause the JTAG to become lost and confused at least.

  • Hi Ralph,

    In light of today's (earlier) "attack" - how refreshing to enjoy the "proper exchange" of tech issues/understandings...

    Again - no one disputed your direction - yet just as you note - poster's description is NOT especially comforting - and "much" remains (still) "Up in the Air!" When firm/I "raise money for tech" such issues - most always - are "deal-breakers." Best to "get your users "Used to dealing in hard, real and sustained FACTS" - which (as you so often seek) - may be replicated by others - thus proving (almost always) their validity. (cash register (only then) emits its pleasant (and often SAVING) RING!) Some may note such "attention to detail" does not (fully) "Paint cb1 as the "Bad Guy!"

    I'm off to our external warehouse w/crüe - yesterday was "all time heat record in Chi." (above 90°F again today...) (see cb1 "sweat" both "on the forum" and "moving the warehouse...")
  • Hello,

    Using MCU Clock prom PIOSC and setting ADC Clock from PIOSC in the polling example, as well, as while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)){} has not worked.

    And yes, I was originally using the PIOSC to clock the MCU, also tried using MOSC at 25MHz, and for some codes and time, they did work perfectly, but stopped shortly after. Using the PLL seems to be more reliable, at least for now. 

  • Hello Luiz,

    Did you use the SysCtlPeripheralReady API directly *AFTER* using the SysCtlPeripheralEnable API? If so that would be disturbing if it did not work and point to some other underlying issue...

    Again the order should be:

        //
        // The ADC0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0));

  • Or better


    // // The ADC0 peripheral must be enabled for use. // SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)){ }

    Robert

  • Yes, I did, and also I resetted the ADC as well: 

    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
    
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)) {
    
    }