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/TM4C1294NCPDT: Read Temperature Data via ADC0SS3 using ADC interrupt

Part Number: TM4C1294NCPDT

Tool/software: Code Composer Studio

I am working on a project in which I'm attempting to read from an external Temperature Sensor (TMP36) using ADC module 0 Sample Sequencer 3.

I am connecting the sensor to PD2 for AIN13

The first sample is processor triggered and then I set it to be continuously sampled in the interrupt handler.

I walked through the data sheet multiple times and am not sure what I am doing wrong in my code. It seems that in my code I never enter my interrupt handler and therefore never get data from the sensor but I'm not sure how to fix that. I used a test value to see if I ever entered the interrupt handler and the value never updated.

At this point I am not even sure where the issue may lie, I've been debugging for over a week and am not really sure what to do next.

// this file assumes the temperature sensor
// uses analog channel 13 to do the AD Conversion
// the voltage reflecting the measured temperature
// can be calculated by reading the register ADC0_SSFIFO3_R
// where only the lowest 12 bits are accesible
// Since AN#13 is multiplexed with GPIO Pin D2
// so relevant GPIO Port D configuration is needed

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

// providing clock to GPIO module
#define SYSCTL_RCGCGPIO_R       (*((volatile unsigned long *)0x400FE608))

// providing clock to ADC module
#define SYSCTL_RCGCADC_R        (*((volatile unsigned long *)0x400FE638))

// ADC0 and SS3
#define ADC0_ACTSS_R            (*((volatile unsigned long *)0x40038000))   // enable ss3
#define ADC0_IM_R               (*((volatile unsigned long *)0x40038008))   // interrupt mask
#define ADC0_ISC_R              (*((volatile unsigned long *)0x4003800C))   // interrupt clear
#define ADC0_EMUX_R             (*((volatile unsigned long *)0x40038014))
#define ADC0_PSSI_R             (*((volatile unsigned long *)0x40038028))
#define ADC0_SAC_R              (*((volatile unsigned long *)0x40038030))   // oversampling
#define ADC0_SSEMUX3_R          (*((volatile unsigned long *)0x400380B8))
#define ADC0_SSMUX3_R           (*((volatile unsigned long *)0x400380A0))
#define ADC0_SSCTL3_R           (*((volatile unsigned long *)0x400380A4))
#define ADC0_SSFIFO3_R          (*((volatile unsigned long *)0x400380A8))
#define ADC0_PC_R               (*((volatile unsigned long *)0x40038FC4))

// GPIO port D
#define GPIO_PORTD_AHB_DIR_R    (*((volatile unsigned long *)0x4005B400))
#define GPIO_PORTD_AHB_DEN_R    (*((volatile unsigned long *)0x4005B51C))
#define GPIO_PORTD_AHB_AMSEL_R  (*((volatile unsigned long *)0x4005B528))
#define GPIO_PORTD_AHB_AFSEL_R  (*((volatile unsigned long *)0x4005B420))
//#define GPIO_PORTD_AHB_DATA_R   (*((volatile unsigned long *)0x4005B3FC))

// NVIC interrupt module
#define NVIC_EN0_R              (*((volatile unsigned long *)0xE000E100))
uint32_t TempData;
//uint32_t Test = 2;


// This function generates the delay
void Delay ( volatile unsigned int delay ){
    volatile unsigned int i, j;

    for (i = 0; i < delay ; i++) {
        // introduces a delay of about 10 us at 16 MHz
        for (j = 0; j < 12; j ++) ;
  }
}


// ADC0 interrupt service routine for sequencer 3
void ADC0SS3_Handler (void) {
//    Test++;
    ADC0_EMUX_R |= 0xF000;
    ADC0_ISC_R |= 0x08;
    TempData = ADC0_SSFIFO3_R; //& 0x0FFF;

}



// main function
int main ( void ){

    volatile float Temp;
//  1. Enable the clock for ADC0
    SYSCTL_RCGCADC_R |= 0x01;
//  2. Enable the clock to Port D
    SYSCTL_RCGCGPIO_R |= 0x08;

    // configuration of port D pin2
    GPIO_PORTD_AHB_AFSEL_R |= 0x04; // functions as analog let AIN13 work
    GPIO_PORTD_AHB_DEN_R &= 0x00;   // disable digital enable analog
    GPIO_PORTD_AHB_AMSEL_R |= 0x04; // enable analog
    GPIO_PORTD_AHB_DIR_R &= 0x00;   // 0 for input

    // Initiate sequencer 3
    ADC0_PSSI_R |= 0x08;

    // and introduce a delay by call Delay function
     Delay (3);

    // Select AIN13 ( PD2 ) as the analog input
    // 1st sample is end of sequence and source of interrupt
    ADC0_PC_R |= 0x03;          // quarter convertion rate; 48*Tadc periods pause
    ADC0_SAC_R  |= 0x04;        // 16x oversampling and then averaged

    // Unmask ADC0 sequence 3 interrupt
    ADC0_IM_R |= 0xF7;      // step 6
    // Clear the interrupt for ADC0 sequencer 3
    ADC0_ISC_R |= 0x08;


    // Enable ADC0 module for sequencer 3
    ADC0_ACTSS_R &= 0x00; // step 1 disabling sample sequencer 3
    ADC0_EMUX_R &= 0x0000;
    ADC0_SSEMUX3_R = 0x0;
    ADC0_SSMUX3_R |= 0x0D;  // step 4 select AIN13
    ADC0_SSCTL3_R |= 0x06;  // step 5
    ADC0_IM_R |= 0x08;      // step 6
    ADC0_ACTSS_R |= 0x08;   // step 7 enabling sample sequencer 3
    ADC0_ISC_R |= 0x08;


    // Enable ADC0 sequencer 3 interrupt in NVIC interrupt number is 17
    NVIC_EN0_R |= 0x020000;

    // wait certain time for ADC module do the conversion
    Delay(100);

    while (1) {
    }
}




  • Hello Lubov,

    Per our TM4C Forum Guidelines, we do not offer support for Direct Register programming as you are doing for TM4C devices on E2E (see item #4): e2e.ti.com/.../695568

    You need to use TivaWare which has been provided by TI to remove the complexities of what you are trying to do.

    We have ADC examples in TivaWare, located at [Install Path]\TivaWare_C_Series-2.1.4.178\examples\peripherals\adc

    That would be a good place to begin working from.
  • Hi Ralph,

    I looked at the example and non of them implemented an interrupt handler in the code. I see that they do use ADCIntStatus() and also clear the interrupt.
    I have implemented my code using the TivaWare and it worked but I didn't implement my own interrupt handler.
    I guess my question would be, is there a way to use the TivaWare functions and implement your own interrupt handler?
  • Hello Lubov,

    Yes definitely. That is always the intent for TivaWare. What you would do is create a function for your interrupt handler, say "ADC0_ISR" and write your interrupt handler in that function.

    Then you would go to the startup_ccs.c file for your project and include that function into the file by using the extern keyword like this:

    extern void ADC0_ISR(void);

    And then you would need to find the corresponding ADC interrupt in the interrupt vector table and replace IntDefaultHandler with ADC0_ISR to link your interrupt handler function to the interrupt vector table.

  • Yes, I did all that and it still didn't work.

    I never had an issue implementing hardware or timer triggered interrupts but for some reason its not working with the ADC

    If I implement my own interrupt handler would I need to call ADCIntStatus() ?

  • Hello Lubov,

    Why don't you post your code so I can review it and see if I can identify any missing API calls. You can do this easiest by using the Insert Code, Attach Files and more... option and then either upload the file or paste the code using the Syntax Highlight (which is accessed by the </> symbol).
  • Ok so what I was hoping I was doing here was setting the first sample to be processor triggered and the rest to continuous sampling

    #include <stdint.h>
    #include <stdbool.h>
    #include "hw_memmap.h"
    #include "hw_types.h"
    #include "debug.h"
    #include "sysctl.h"
    #include "gpio.h"
    #include "adc.h"
    
    uint32_t TempData[1];
    volatile uint32_t Value;
    
    void ADC0SS3_Handler (void) {
    	ADCIntClear(ADC0_BASE, 3);
    	ADCSequenceDataGet(ADC0_BASE, 3, TempData);
    	Value = TempData[0];
    }
    
    int main(void)
    {
    
    
    	SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    
    	GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_2);
    
    	ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);
    	ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);
    
    	ADCSequenceEnable(ADC0_BASE, 3);
    
    	ADCIntClear(ADC0_BASE, 3);
    	ADCIntEnable(ADC0_BASE, 3);
    	ADCProcessorTrigger(ADC0_BASE, 3);
    
    	while(1)
    	{
    		ADCIntClear(ADC0_BASE, 3);
    		ADCIntRegister(ADC0_BASE, 3, ADC0SS3_Handler);
    	}
    }

  • Hello Lubov,

    Thanks for posting the code, very helpful to see what is going on. A few comments here.

    1) The ADCIntRegister needs to be done as part of the configuration before ADCIntEnable is called.

    2) When using interrupt service routines, you shouldn't do ADCIntClear outside of the ISR (there may be occasional situations one may do this, but it would be to handle errors and such). The ADCIntClear usage in the example code was because they were polling, not using an ISR.

    3) You need to enable global interrupt via: IntMasterEnable(); - I recommend doing this before ADCIntClear.

    4) If you want to do continuous sampling then you either need to call ADCProcessorTrigger(ADC0_BASE, 3); in the ISR, or you need to use the ADC_TRIGGER_ALWAYS flag rather than ADC_TRIGGER_PROCESSOR when configuring your sequence. In this case, you may also want to move the ADCSequenceEnable(ADC0_BASE, 3); call to be when you want to trigger the first result, so probably at the line you call ADCProcessorTrigger.