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.

ADC Sequencer Interrupt Help

Other Parts Discussed in Thread: TM4C123GH6PM

I am trying to get Lab 5 from the Tiva launch pad workshop to work with interrupts.  I am using a Stellaris launchpad.  The idea of the lab was to just keep polling the ADCIntStatus until the sequencer was full and then process the values.  However I wanted to try to have an interrupt tell me when sequencer was full.  I am not sure if I have the interrupt set up correctly, or if I need to do sequencer different.

My code is below.

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#define TARGET_IS_BLIZZARD_RB1
#include "driverlib/rom.h"
#include "driverlib/interrupt.h"

#ifdef DEBUG
void__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//used for storing data from ADC FIFO, must be as large as the FIFO for sequencer in use. Sequencer 1 has FIFO depth of 4
uint32_t ui32ADC0Value[4];

//variables that cannot be optimized out by compiler
volatile uint32_t ui32TempAvg;
volatile uint32_t ui32TempValueC;
volatile uint32_t ui32TempValueF;
volatile uint32_t status;

void ADC0IntHandler(void)
{
     //clear interrupt flag
     ROM_ADCIntClear(ADC0_BASE, 1);

     ROM_ADCSequenceDataGet(ADC0_BASE,1,ui32ADC0Value);
     //calculate average
     ui32TempAvg = (ui32ADC0Value[0] + ui32ADC0Value[1] + ui32ADC0Value[2] + ui32ADC0Value[3] + 2)/4;
     //TEMP = 147.5 – ((75 * (VREFP – VREFN) * ADCVALUE) / 4096) multiply by 10 to keep precision and then div by 10 at end
     ui32TempValueC = (1475 - ((2475 * ui32TempAvg)) / 4096)/10;
     ui32TempValueF = ((ui32TempValueC * 9) + 160) / 5;
}

int main(void)
{

    //config sysClock to run at 40MHz
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

     //enable ADC0 peripheral
     ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
     //configure number of samples to be averaged
     ROM_ADCHardwareOversampleConfigure(ADC0_BASE, 64);

     //configure ADC sequencer - use ADC 0, sample sequencer 1, want the processor to trigger sequence, use highest priority
     ROM_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);

     //Configure ADC Sequencer Steps 0 - 2 on sequencer 1 to sample the temperature sensor
     ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_TS);
     ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_TS);
     ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_TS);

     //final sequencer step: need to sample temp sensor(ADC_CTL_TS), configure interrupt flag(ADC_CTL_IE) to be set when sample is done
     //tell ADC logic that this is the last conversion on sequencer (ADC_CTL_END)
     ROM_ADCSequenceStepConfigure(ADC0_BASE,1,3,ADC_CTL_TS|ADC_CTL_IE|ADC_CTL_END);

     ROM_ADCSequenceEnable(ADC0_BASE,1);

     //enable interrupt, may need to enable processor interrupt:
     IntMasterEnable();
     ADCIntClear(ADC0_BASE,1);
     ADCIntEnable(ADC0_BASE,1);
     //IntEnable(INT_ADC0);       //This line isn't working either

     //clear interrupt flag
     ADCIntClear(ADC0_BASE, 1);

     while(1)
     {

            //trigger ADC conversion
            ROM_ADCProcessorTrigger(ADC0_BASE, 1);
            volatile int32_t status = status = ADCIntStatus(ADC0_BASE,1,false);

     }

}

Thank you for any help.

  • Hello Jarrod,

    Maybe you can check:

    1. the interrupt handler is at the right place in the vector table. Try put a brakpoint somewhere in ADC0IntHandler(), see if it is reachable.
    2. where the ADC sampling rate configuation is. Use SysCtlADCSpeedSet() function to configure speed.

    Regards,

    Jo

  • This example should get you started:

    http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/t/212263.aspx

    I have a much improved example which I have never published and a version which talks to both ADC and a TWI accelerometer -- which does similar things with interrupts. However, you will need to put a scope on it and develop a good understanding of how the interrupt system works -- from a time use point of view.

  • Jo-

    I have checked the startup_ccs.c file and have included a snippet of the file below.  I believe I have put the ADC0IntHandler in the correct vector and also included it external declarations above vector table.

    //*****************************************************************************
    //
    // External declaration for the reset handler that is to be called when the
    // processor is started
    //
    //*****************************************************************************
    extern void _c_int00(void);
    extern void ADC0IntHandler(void);

    Vector Table snippet

    IntDefaultHandler,           // Quadrature Encoder 0
    ADC0IntHandler,             // ADC Sequence 0
    ADC0IntHandler,             // ADC Sequence 1
    ADC0IntHandler,             // ADC Sequence 2
    ADC0IntHandler,             // ADC Sequence 3

    I have also tried putting a break point in ADC0IntHandler() and the program never reaches the function.  I have also slowed down sampling frequency to 250KSPS. using the code below to check if sample rate has changed

    unsigned long ulSampleRate = SysCtlADCSpeedGet();
    SysCtlADCSpeedSet(SYSCTL_ADCSPEED_250KSPS);
    unsigned long ulSampleRate2 = SysCtlADCSpeedGet();

    //configure ADC sequencer - use ADC 0, sample sequencer 1, want the processor to trigger sequence, use highest priority
    ROM_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);

    //Configure ADC Sequencer Steps 0 - 2 on sequencer 1 to sample the temperature sensor
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_TS);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_TS);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_TS);

    Thank you again for any help.

    -Jarrod

  • There is one point missed in your code and please add the following after ROM_SysCtlPeripheralEnable() call:

    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5);

    The above is just an example. Check the datasheet and find out which pin you are using in the hardware.

  • The link below will take you to a wiki page with downloads of lab documents and downloads at bottom of page.

    http://processors.wiki.ti.com/index.php/Getting_Started_with_the_TIVA%E2%84%A2_C_Series_TM4C123G_LaunchPad

    I have checked the interrupt flag (ADC_ISC_IN1) and interrupt mask (ADC_IM_MASK1) and they are both set to 1, but still no entry into the ISR.

  • Jarrod,


    I would start trying to debug this by sticking with the simplest functionality, so maybe remove the oversampling, and try just doing one step at a time. The other possible issue is that you're constantly sending a processor trigger for starting the conversion, so I would revert to maybe having the processor trigger execute before the while loop, then maybe call it again at the end of your ISR.

    -Reagan

  • Reagan Revisore said:
    I would start trying to debug this by sticking with the simplest functionality

    So true - and so often violated.  Good for you for presenting.

    KISS - and proceed by refinement - so often save time/effort - and yield more robust and more "reusable" code blocks...

    Final point - code presented is so specific - so detailed - and most likely could be reduced to its "essence" prior to presentation - here..  Simplicity serves to isolate - spectacularly eases the review & recognition of code and/or usage faults - which are well camouflaged by existing (overly detailed) code listings...

    Update/Note: two posts down - poster's issue found - justified - and solved!

  • One more point to add to the above.  In the attempt to further isolate the sequence of events which occur - just post an ADC "Trigger" (either via PWM Gen. or Timer) we removed the ADC_CTL_IE parameter from our last step sequence.  (only occurrence of that parameter)  Our thinking - now there was no obvious mechanism to cause entry to our ADC0 interrupt handler. 

    And then we generated first PWM generated and then Timer generated "triggers" - and in each case we reached our break-point - early w/in the ADC0 interrupt handler!

    Beyond this - careful, repeated study of both MCU manual and SW-DRL-UG (multiple incarnations) have never/ever revealed any description of just what constitutes an ADC "trigger" - from any source.  We looked at the source code w/in pwm.c, timer.c and adc.c too - and iirc no comforting nor amplifying data resulted from that effort.

    Inside info - describing what reasonably can be expected to happen upon an "ADC Trigger" - to include function sequence listings - much appreciated and may serve to resolve poster's issue as well...  Thanks in advance for any detail which better explains. 

  • Kindly note - post above more rambling/disjointed than usual.  Pardon - joy of "in transit" - compose on the fly - and "cut/paste" not up to usual, Gov't Standard.  (and from this morning's road location - powerless to modify...)

    Poster states, "I have checked the interrupt flag (ADC_ISC_IN1) and interrupt mask (ADC_IM_MASK1) and they are both set to 1, but still no entry into the ISR."  No mention here of equally important Register "ADC0RIS" - would expect ADC_ACTSS_ASEN1 bit to also be set = 1.

    And Aha - here's your issue: ROM_IntEnable(INT_ADC0SS1); should replace your  //IntEnable(INT_ADC0);  //This line isn't working either *** bet it will, now!  Your use of ADC0 has been deprecated - and refers to Sequence 0 - not your intended Sequence 1!   Q.E.D.  (Green Verify Answer tick - s'il vous plait...)

    Note further: you "intermingle" both ROM and Flash versions of ADC code.  Suspect that ROM-only functions will work - surely does on our LX4F - and will reduce "code bloat..."

    Warning: Having solved poster's issue - thread now "drifts" from inability to ever enter ADC interrupt - into the loss of that ADC interrupt's entry - after thousands (actually hundreds of thousands) of successful entries!

    The "ADC Trigger" mechanism works very well when ADC interrupts are not employed.  In fact - when any of a number of ADC Triggers are initiated - the ADC correctly executes.

    With bit more time to re-think - what I now believe to be most helpful would be advice/guidance as to "where and how" users may insert break-points (or via other means/methods) to confirm, the "what/where" of "first action program entries" - upon an ADC Trigger.  (i.e. just what program code is executed immediately after the ADC Trigger occurs?)   (that trigger may emanate from PWM Gen, Timer or Processor - perhaps others (i.e. comparators))

    Again - rather exhaustive search of "usual suspect" literature finds minimal (no!) description of the ADC Trigger mechanism. 

    In the attempt to help ourselves - the ADCPSSI Register appeared fruitful: (alas key bits are write only - defying our ability to confirm that trigger had potentially occurred and/or been "recognized' by the ADC.   Look here:

    This inability to confirm the "success" of a trigger - plagues our, "ADC failure after days successful run" - troubleshooting..)  Might someone here - suggest an alternate method so that an ADC Trigger may be someway/somehow confirmed?  (further detail - for those interested - below...)

    We have attempted self-diagnosis via the inclusion of SW counters both w/in our PWM Generator code blocks, Timer code blocks, and the ADC interrupt.  Both the PWM Generator and Timer SW counters always continue to advance (in some cases - exceeding 1 billion counts) prior to the ADC's ceasing its operation.  (this noted by the freezing of the SW counter placed early w/in the ADC interrupt service routine.  Again - this effect is somewhat  (but not exactly) similar to an errata mention - our LX4F device.

    Apologies - I have wandered from this thread's focus (after solving poster's issue) - but should something herein shed light - forum group may benefit from enhanced understanding of this ADC Trigger mechanism...  And that would be good - perhaps save many from descent into our unwelcome/unexpected ADC halting, plight... 

  • Yes you are correct. I just found this out myself when I implemented a timer to trigger the ADC.

    I needed to  #include "inc/tm4c123gh6pm.h" in order to use INT_ADC0SS1

    and also enable the specific vector in the NVIC by using IntEnable(INT_ADC0SS1)

    Thank you for everyone's help and comments.

    -Jarrod

  • Good for you - glad you persisted - and thanks.

    You may wish to switch to the ROM ADC functions - to insure that all "made it" there successfully.  "Mix" of ROM and non-ROM - while not harmful (other than adding to flash usage) - will likely impinge upon your future code use.  The "inconsistencies" will always "disturb" - and may blind you to required "intermix." (i.e. GPIOPinTypeI2CSCL currently resides only w/in flash version)

    Note that we find INT_ADC0SS1 w/in file hw_ints.h - not our LX4F.h file - as you describe.  (we use StellarisWare - thus may have changed - you may wish to check/confirm...)  Indeed you must "tell" the IDE which MCU you are targeting - as you state - but I find the migration of that "INT_ADC0SS1" definition unlikely!

    Note further that errata exists - impacting your "MCU internal temp. sense" (you must "throw out" initial two conversions).  Suspect that introducing a known, external voltage - after configuring the appropriate port/pin as ADC input(s) - will better enhance your ADC efforts...  Take care...

  • It's the line "IntEnable(INT_ADC0SS1)" that did it for me.
    I was using Sequence 3, but had this set to IntEnable(INT_ADC0SS0).
    Changed to IntEnable(INT_ADC0SS3) and voila!  The ADC ISR triggered.