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.

LM4F232 ADC0 Issue

Hi!

I'm using a LM4F232 Evaluation Kit and have come across some strange behavior in ADC module, not sure if it is a hw issue or 

just me. I'm setting up an ADC to sample 3 channels at constant rate (via timer trigger). Every now and then, when I reset 

the board after programming, the mcu enters NMI just after SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); The set up code is as follows:

 

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1);

SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);

SysCtlDelay(1000); // delay, otherwise a wild NMI appears now and then

ADCReferenceSet(ADC0_BASE, ADC_REF_EXT_3V);

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // erratum 6.1

HWREG(GPIO_PORTB_BASE + GPIO_O_AMSEL) |= GPIO_PIN_6; // erratum 6.1

ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);

ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0 );

ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1 );

ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_IE | ADC_CTL_END);

ADCSequenceEnable(ADC0_BASE, 0);

ADCIntEnable(ADC0_BASE, 0);

 

 

 

When I insert a small delay just after enabling and resetting the module, the issue dissapears and the module operates

as intended. No sure what is causing the issue here. I'm running at 80Mhz:

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

 

Timer which controls aquisition rate is configured as 32 bit timer with interrupt on timeout

 

SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

SysCtlPeripheralReset(SYSCTL_PERIPH_TIMER0);

TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);

TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()/ADC_RATE);

TimerControlTrigger(TIMER0_BASE, TIMER_A, true);

TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

TimerEnable(TIMER0_BASE, TIMER_A);

 

 

Interrupt routine for time just clears the interrupt source. While the interrupt routine for ADC clears the source and fetches data:

 

void ADCIntHandler (void)

{

ADCIntClear(ADC0_BASE, 0);

samples_aquired = ADCSequenceDataGet(ADC0_BASE, 0, &adc_data[0]);

}

 

 

Another thing I've noticed is that  ADCSequenceOverflow(ADC0_BASE, 0) always returns 1, even if capture rate is below 500Hz (ADC_RATE). Not sure why this occurs, running at 80Mhz gives me plenty of time to empty the FIFO in the ISR. 

Any ideas?

P.S I'm using Keil 4.22a

 

  • Hello antoker,

    Is this NMI reproducible? Do you consistently get the NMI if you remove that delay? How frequently does it happen? Do you ever get the NMI with that delay?

    Can you show me the first instructions you execute? Like setting up the clock, ect, in sequential order.

    The overflow condition seems odd to me. Just for a test try reducing the sample rate until you no longer get the overflow condition and let me know when that occurs roughly.

    Regards,

    Alex

  • Hi Alex, 

    Now I know that is no a NMI. Because, when mcu hangs I try to enter debugging mode and the first thing I see in disassembly is NMI, but then I can step through code as normal, so I assume the debugger resets the mcu and I can't really tell what has happened to the mcu prior to entering the debug mode. 

    What I've found out is:

    1. SysCtlDelay(1000); after SysCtlPeripheralEnable completly cures the issue. I've reset the mcu about 10-15 times and each time it works.

    2. Removing the delay causes the mcu to hang about once per 3-4 resets. 

    Regarding the overflow:

    I think I found out what is wrong here, first, I've used a wrong startup.s file. Second, I've enabled an interrupt via NVIC for INT_ADC0 and not INT_ADC0SS0. So the issue with overflow when triggering via timer is gone completely, sorry about that.

    I still getting overflow when attempting to use ADC_TRIGGER_PROCESSOR and re-triggering conversion via ISR from a timer, that way I can go up to 10kHz before overflowing. 

    Here is the complete code. You can run it directly on the kit. Uart is redirected via debug interface. 

    http://dl.dropbox.com/u/113912/adc_sampler.zip

  • antoker,

    I ran your code and saw similar behavior - and I know why.

    When you enable / reset a peripheral there is a slight time delay before you can access the peripheral without causing a fault. This is typically 5 cycles. Since you access the ADC right after enabling and resetting it you are getting a fault (the fault occurs because you try to access a peripheral before it has been fully enabled).

    What I did was add the following line to your code after you reset the peripheral:

    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)){}

    and it works fine.

    You had the right idea with the delay, now I hope you understand why that fixed it.

    Regards,

    Alex

  • Alex, thank you for the explanation. 

  • hi sir, 

           i am new to the community,i hope you will solve my pbm.sir i'm interfacing uart and adc but i am getting a problem like intfault handler (when ever the service reaches the ROM_ADCProcessorTrigger(ADC0_BASE, 3);in my code). may i know why it occurs and how to rectify the pbm.currently i'm working on LM4F controller and i'm new to it.can you please help me with your valuable solution ASAP. my code flow is

    //*****************************************************************************


    //
    // This is part of revision 8028 of the EK-LM4F232 Firmware Package.
    //
    //*****************************************************************************

    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/rom.h"
    #include "grlib/grlib.h"
    #include "te_adc.h"
    #include "driverlib/adc.h"

    //#include "uartstdio.h"
    #include "drivers/cfal96x64x16.h"
    void
    UARTSend(const unsigned char *pucBuffer, unsigned long ulCount);
    #define UART_INPUT_CTS 0x00000001

    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
    }
    #endif

    void delay(unsigned long loop)
    {
    unsigned long l,h;
    for(l=0;l<=loop;l++)
    for(h=0;h<10000;h++);
    }
    //*****************************************************************************
    //
    // The UART interrupt handler.
    //
    //*****************************************************************************
    void
    UARTIntHandler(void)
    {
    unsigned long ulStatus;
    unsigned char shyam;

    // Get the interrrupt status.
    //
    ulStatus = ROM_UARTIntStatus(UART0_BASE, true);
    //
    // Clear the asserted interrupts.
    //
    ROM_UARTIntClear(UART0_BASE, ulStatus);

    //
    // Loop while there are characters in the receive FIFO.
    //
    while(ROM_UARTCharsAvail(UART0_BASE))
    {
    //
    // Read the next character from the UART and write it back to the UART.

    shyam = ROM_UARTCharGetNonBlocking(UART0_BASE);
    ROM_UARTCharPutNonBlocking(UART0_BASE, shyam);
    //
    if(shyam == 's')
    {
    UARTSend((const unsigned char *)" : Command Mode:\n", 17);
    delay(6);
    UARTSend((const unsigned char *)"data mode:\n ", 28);
    delay(6);
    //a=adc_ReadChannel(ADC0_BASE, ADC_CTL_CH0, sADC0_Value, &fADC0_Value);

    }
    }
    }

    //*****************************************************************************
    //
    // Send a string to the UART.
    //
    //*****************************************************************************
    void
    UARTSend(const unsigned char *pucBuffer, unsigned long ulCount)
    {
    //
    // Loop while there are more characters to send.
    //
    while(ulCount--)
    {
    //
    // Write the next character to the UART.
    //
    ROM_UARTCharPutNonBlocking(UART0_BASE, *pucBuffer++);
    }
    }

    //*****************************************************************************
    //
    // This example demonstrates how to send a string of data to the UART.
    //
    //*****************************************************************************
    int
    main(void)
    {


    /*FPUEnable();
    FPULazyStackingEnable();*/

    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
    SYSCTL_XTAL_16MHZ);
    //ConfigInit();
    //
    // Enable the peripherals used by this example.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Enable processor interrupts.
    //
    ROM_IntMasterEnable();

    //
    // Set GPIO A0 and A1 as UART pins.
    //
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Configure the UART for 115,200, 8-N-1 operation.
    //
    ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), 115200,
    (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
    UART_CONFIG_PAR_NONE));

    //
    // Enable the UART interrupt.
    //
    ROM_IntEnable(INT_UART0);
    ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);

    //
    // Prompt for text to be entered.
    //
    //UARTSend((unsigned char *)"Enter text: ", 12);

    //
    // Loop forever echoing data through the UART.
    //
    TestADCinput();


    while(1)
    {
    //
    read_adc();
    }
    }

    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_adc.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/adc.h"
    #include "te_adc.h"
    #include "driverlib/uart.h"
    #include "driverlib/gpio.h"
    #include "driverlib/uart.h"
    #include "driverlib/rom.h"
    float fadc = 0;
    unsigned int i;
    unsigned long ulADC0_Value;

    void
    TestADCinput(void)
    {
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

    //
    // For this example ADC0 is used with AIN0 on port E7.
    // 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.
    //
    ROM_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.
    //

    ROM_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
    //
    // 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.
    //
    ROM_ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);

    //
    // Since sample sequence 3 is now configured, it must be enabled.
    //
    ROM_ADCSequenceEnable(ADC0_BASE, 3);


    //
    // Clear the interrupt status flag. This is done to make sure the
    // interrupt flag is cleared before we sample.
    //
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);
    ADCIntEnable(ADC0_BASE, 0);
    ROM_ADCIntClear(ADC0_BASE, 3);

    }

    void read_adc(void)
    {
    // Trigger the ADC conversion.
    //
    ROM_ADCProcessorTrigger(ADC0_BASE, 3);
    //
    // Wait for conversion to be completed.
    //
    while(!ROM_ADCIntStatus(ADC0_BASE, 3, false))
    {
    }
    //
    // Read ADC Value.
    //
    ROM_ADCSequenceDataGet(ADC0_BASE, 3, &ulADC0_Value);
    fadc = ((ulADC0_Value) * ((float)3/(float)1024) * 4);
    while(i--)
    {
    //
    // Write the next character to the UART.
    //
    ROM_UARTCharPutNonBlocking(UART0_BASE, fadc++);
    }

    }