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.

TM4C123GH6PM: ADC Comparator Interrupt

Part Number: TM4C123GH6PM

Tool/software:

Hello,

I am trying to implement an ADC comparator interrupt so that the Red LED turns on if the measured voltage is higher than 0.11V and turns off when it is lower than 0.11V, which is about 136/4096 * 3.3V. Below is the code I have. I suspect that something is wrong with the interrupt conditions/configurations in the InitADC and ADC0Seq2Handler. I have correctly updated the vector table in the startup_ccs.c. 

#define ADC_SEQUENCER3_LENGTH 1
#define ADC_SEQUENCER2_LENGTH 4
#define ADC_SEQUENCER1_LENGTH 4
#define ADC_SEQUENCER0_LENGTH 8
#define VOLTAGE_PRIORITY 0
#define VOLTAGE_CALIBRATION_FACTOR 1.008

uint32_t VoltBuf[ADC_SEQUENCER2_LENGTH];                        // SS2 has the size of 4 samples
uint32_t voltage;
int32_t VoltIntegerPart;
int32_t VoltFractionPart;

void ADC0Seq2Handler(void){
    IntMasterDisable();
    uint32_t ulStatus = ADCIntStatus(ADC0_BASE, 2, true);
    uint32_t comparatorStatus = ADCComparatorIntStatus(ADC0_BASE);
    ADCIntClear(ADC0_BASE,2);

    if (comparatorStatus & (1 << 0)) {
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
        ADCComparatorIntClear(ADC0_BASE, 0);
    }
    if (comparatorStatus & (1 << 1)) {
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
        ADCComparatorIntClear(ADC0_BASE, 1);
    }
    IntMasterEnable();
}

void InitADC(void){
  SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); 
  GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);                                                          // Configure PE3 as an ADC input
  SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);                                                           // Enable the ADC0 peripheral
  ADCHardwareOversampleConfigure(ADC0_BASE,32);                                                         // Set the auto average to 64

  // Configure sequence 2 for voltage measurement on AIN0 (PE)
  ADCSequenceDisable(ADC0_BASE, 2);                                                                     // Disable sequence 2
  ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, VOLTAGE_PRIORITY);                          // Configure sequence 2
  ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH0);
  ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH0);
  ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH0);
  ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);                     // Set up the last step and start an interrupt when the conversion is over
  ADCSequenceEnable(ADC0_BASE, 2);                                                                       // Enable the sequence again
  ADCIntEnable(ADC0_BASE,0);
  ADCIntClear(ADC0_BASE, 2);                                                                             // Clear the interrupt flag


  // Configure the ADC comparator
  ADCComparatorConfigure(ADC0_BASE, 1, ADC_COMP_TRIG_NONE | ADC_COMP_INT_HIGH_HALWAYS); ////////////////////////////////////////////////////////////////////////////////// could be wrong
  ADCComparatorRegionSet(ADC0_BASE, 0, 136, 4095); // Set the comparator region (136 corresponds to 0.11V)
  ADCComparatorReset(ADC0_BASE, 0, true, true); // Reset the comparator
  ADCComparatorIntEnable(ADC0_BASE, 0); // Enable the comparator interrupt

  ADCComparatorConfigure(ADC0_BASE, 2, ADC_COMP_TRIG_NONE | ADC_COMP_INT_LOW_HALWAYS ); ////////////////////////////////////////////////////////////////////////////////// could be wrong
  ADCComparatorRegionSet(ADC0_BASE, 1, 0, 136); // Set the comparator region (136 corresponds to 0.11V)
  ADCComparatorReset(ADC0_BASE, 1, true, true); // Reset the comparator
  ADCComparatorIntEnable(ADC0_BASE, 1); // Enable the comparator interrupt

  IntEnable(INT_ADC0SS2); // Enable the ADC sequence 2 interrupt in the NVIC
  IntMasterEnable();
}

void InitGPIO(void) {
    // Enable the GPIO port for the LED (PF2)
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1); // Configure PF2 as an output
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0); // Initialize the LED to be off
}


void main(void) {
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // System clock: 16*12.5/2.5 = 80MHz (MAX)
    InitADC();
    InitGPIO();
    while(1){}
}





Right now, PE3 is collecting the voltage data and I verified that it's working fine. What's wrong?

  • Line 44 should be ADCIntEnable(ADC0_BASE,2); by the way

  • Actually, that kills the rest of the main functions I have.

  • Let's say you want to pass AIN0 to the Comparator 0 for comparison you will write something like below. 

    ADCSequenceStepConfigure( ADC0_BASE, 2, 0, ADC_CTL_CH0 | ADC_CTL_CMP0 ); // Assign CH0 to step 0 and route this channel to Comparator 0. 

    In your code, I don't see assign any channel to any comparator while I see you use Comparator 1 and Comparator 2 for checking the the bounds. Bear in mind that associated with each comparator unit, you can set up a upper and lower bond values. See below description. There is no need to use two different comparator units to setup the upper and lower bounds. Its seems like you were intending to use two different comparator units to setup two different bound values. In another, you just need to assign a channel (e.g. CH0) to a comparator (e.g. ADC_CTL_CMP0 ) and setup the lower and upper bounds for ADC_CTL_CMP0 comparator . 

      

  • Thank you for your response. The updated code is shown below. It is still not working and I have no clue what's wrong. I've been following the example codes shown here : https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/313435/tiva-digital-comparator-problem

    But it seems like their example does not have the internal temperature sensor reading and mine does, and I don't know which part of my set up is wrong. By the way, if I run the below code, I get the correct output on the terminal. Just the ADC interrupt is not working. 

    #define ADC_SEQUENCER3_LENGTH 1
    #define ADC_SEQUENCER2_LENGTH 4
    #define ADC_SEQUENCER1_LENGTH 4
    #define ADC_SEQUENCER0_LENGTH 8
    
    #define VOLTAGE_CALIBRATION_FACTOR 1.008
    
    int ulTemp_ValueC;
    uint32_t TempBuf[ADC_SEQUENCER3_LENGTH];                        // SS3 has the size of 1 sample
    uint32_t VoltBuf[ADC_SEQUENCER2_LENGTH];                        // SS2 has the size of 4 samples
    
    int tempC;
    int tempF;
    uint32_t voltage;
    int32_t VoltIntegerPart;
    int32_t VoltFractionPart;
    
    void InitUART(void){
        // Initialize the UART.
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
        UARTStdioConfig(0, 115200,  16000000);
    }
    
    void InitADC(void){
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
      GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);                                                          // Configure PE3 as an ADC input
      SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);                                                           // Enable the ADC0 peripheral
      ADCHardwareOversampleConfigure(ADC0_BASE,32);                                                         // Set the auto average to 64
    
      // Configure sequence 3 for internal temperature sensor
      ADCSequenceDisable(ADC0_BASE, 3); // Disable sequence 3
      ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END); // Set up the last step and start an interrupt when the conversion is over
      ADCSequenceEnable(ADC0_BASE, 3); // Enable the sequence again
      ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 1); // Configure sequence 3, Priority must be 1 or 0 if ADC_TRIGGER_ALWAYS is used by another sequence
      ADCIntClear(ADC0_BASE, 3); // Clear the interrupt flag
    
      // Configure sequence 2 for voltage measurement on AIN0 (PE)
      ADCSequenceDisable(ADC0_BASE, 2);                                                                     // Disable sequence 2
      ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH0 | ADC_CTL_CMP0);
      ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH0);
      ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH0);
      ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);                     // Set up the last step and start an interrupt when the conversion is over
      ADCSequenceEnable(ADC0_BASE, 2);                                                                       // Enable the sequence again
      ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_ALWAYS, 3);                          // Configure sequence 2
      IntPrioritySet(INT_ADC0SS2, 0);
    //  ADCIntEnable(ADC0_BASE, 2);
      ADCIntEnableEx(ADC0_BASE, ADC_INT_DCON_SS2);
      ADCIntClear(ADC0_BASE, 2);                                                                             // Clear the interrupt flag
    
    
      // Configure the ADC comparator
      ADCComparatorConfigure(ADC0_BASE, 0, ADC_COMP_INT_MID_ONCE);
      ADCComparatorRegionSet(ADC0_BASE, 0, 136, 4095); // Set the comparator region (136 corresponds to 0.11V)
      ADCComparatorReset(ADC0_BASE, 0, true, true); // Reset the comparator
      ADCComparatorIntEnable(ADC0_BASE, 2); // Enable the comparator interrupt
    
      IntEnable(INT_ADC0SS2); // Enable the ADC sequence 2 interrupt in the NVIC
      IntMasterEnable();
    }
    
    void InitGPIO(void) {
        // Enable the GPIO port for the LED (PF2)
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1); // Configure PF2 as an output
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0); // Initialize the LED to be off
    }
    
    
    int GetInternalTemp(void){
        ADCProcessorTrigger(ADC0_BASE, 3);                                 // Trigger the ADC conversion process.
        while(!ADCIntStatus(ADC0_BASE, 3, false)) {}                       // Wait for the interrupt flag to get set
        ADCIntClear(ADC0_BASE, 3);                                         // Clear the interrupt flag
        ADCSequenceDataGet(ADC0_BASE, 3, TempBuf);                     // Get the actual data samples from ADC0 sequencer 3
    
        // Convert the value to temperature
        // TEMP = 147.5 - ((75 * (VREFP - VREFN) * ADCVALUE) / 4096)
        // 3.3V * 10 * 75 = 2475 //13.3.6 of the TM4C123GH6PM datasheet
        ulTemp_ValueC = (1475 -((2475 * TempBuf[0])) / 4096)/10;
    
        return ulTemp_ValueC;
    }
    
    uint32_t GetVoltage(void){
        uint32_t ulADC0Value[1];
    
        ADCProcessorTrigger(ADC0_BASE, 2); // Trigger the ADC conversion process
        while(!ADCIntStatus(ADC0_BASE, 2, false)) {} // Wait for the interrupt flag to get set
        ADCIntClear(ADC0_BASE, 2); // Clear the interrupt flag
        ADCSequenceDataGet(ADC0_BASE, 2, VoltBuf); // Get the actual data samples from ADC0 sequencer 2
    
        ulADC0Value[0] = (VoltBuf[0] + VoltBuf[1] + VoltBuf[2] + VoltBuf[3] + 2)/4;
        return ulADC0Value[0];
    }
    
    void IntToFloat(uint32_t intV){
        float fVoltage;
    
        fVoltage = (intV * 3.3)/4096 * VOLTAGE_CALIBRATION_FACTOR;
        VoltIntegerPart = (int32_t) fVoltage;
        VoltFractionPart = (int32_t) (fVoltage * 1000.0f);
        VoltFractionPart = VoltFractionPart - (VoltIntegerPart * 1000);
        if (VoltFractionPart < 0){
            VoltFractionPart *= -1;
        }
    }
    
    void ADC0Seq2Handler(void){
     //   IntMasterDisable();
     //   uint32_t ulStatus = ADCIntStatus(ADC0_BASE, 2, true);
     //   uint32_t comparatorStatus = ADCComparatorIntStatus(ADC0_BASE);
        uint32_t status;
    
        status = ADCComparatorIntStatus(ADC0_BASE);
    
    
        ADCComparatorIntClear(ADC0_BASE, status);
    
        if ((ADCSequenceDataGet(ADC0_BASE, 2, VoltBuf) >= 136) & status) {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
        }
        if ((ADCSequenceDataGet(ADC0_BASE, 2, VoltBuf) < 136) & status) {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
        }
    }
    
    void main(void) {
        SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); // System clock: 16*12.5/2.5 = 80MHz (MAX)
    
        InitGPIO(); //// change later
        InitADC();
        InitUART();
        
        while(1){
            tempC = GetInternalTemp();
            tempF = (1.8 * ulTemp_ValueC) + 32;                            // Celsius to Fahrenheit
    
            voltage = GetVoltage();
            IntToFloat(voltage);
            UARTprintf("Voltage = %3d.%03dV\n", VoltIntegerPart, VoltFractionPart);
        
            UARTprintf("Temperature = %3d°C / %3d°F\n", tempC, tempF);
            SysCtlDelay(SysCtlClockGet() / 3); // Delay for about 2 seconds
        }
    }
    
    

  • But it seems like their example does not have the internal temperature sensor reading and mine does,

    Hi,

      Please be aware of ADC errata related to temperature sensor reading. 

    By the way, if I run the below code, I get the correct output on the terminal. Just the ADC interrupt is not working.

    Also refer to the below errata.

      

  • Hi Scott,

      Is this issue resolved? I will close the thread for now. If you have any update, you can write to this post again the the thread will change its status to OPEN.