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.

Tiva Digital Comparator Problem

Other Parts Discussed in Thread: TM4C1233H6PM, TM4C123GH6PM

I'm using the TM4C1233H6PM and attempting to use the built in digital comparators to alert me when either a high or low threshold is crossed on three different analog accelerometer channels. The following code is a very much simplified example code from my application. You can assume all peripherals are being enabled correctly prior to the following code being executed. This is because if I use only a single channel and two digital comparators to watch for low or high thresholds it works correctly. This includes any combination of comparators and channels. However the following does not work it will always tell me comparator 0 and 1 are triggered despite the stimulus. Any help would be much appreciated.

ADCSequenceDisable( ADC0_BASE, 0 );

// Set up the ADC trigger to be from the processor (software)
ADCSequenceConfigure( ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0 );

// Set up the ADC interrupt steps
ADCSequenceStepConfigure( ADC0_BASE, 0, 0, XOUT_CHANNEL | ADC_CTL_CMP0 );
ADCSequenceStepConfigure( ADC0_BASE, 0, 1, XOUT_CHANNEL | ADC_CTL_CMP1 );
ADCSequenceStepConfigure( ADC0_BASE, 0, 2, YOUT_CHANNEL | ADC_CTL_CMP2 );
ADCSequenceStepConfigure( ADC0_BASE, 0, 3, YOUT_CHANNEL | ADC_CTL_CMP3 );
ADCSequenceStepConfigure( ADC0_BASE, 0, 4, ZOUT_CHANNEL | ADC_CTL_CMP4 );
ADCSequenceStepConfigure( ADC0_BASE, 0, 5, ZOUT_CHANNEL | ADC_CTL_CMP5 | ADC_CTL_END );

// Configure the comparators
ADCComparatorConfigure( ADC0_BASE, 0, ADC_COMP_INT_LOW_ONCE );
ADCComparatorConfigure( ADC0_BASE, 1, ADC_COMP_INT_HIGH_ONCE );
ADCComparatorConfigure( ADC0_BASE, 2, ADC_COMP_INT_LOW_ONCE );
ADCComparatorConfigure( ADC0_BASE, 3, ADC_COMP_INT_HIGH_ONCE );
ADCComparatorConfigure( ADC0_BASE, 4, ADC_COMP_INT_LOW_ONCE );
ADCComparatorConfigure( ADC0_BASE, 5, ADC_COMP_INT_HIGH_ONCE );

ADCComparatorRegionSet( ADC0_BASE, 0, 1000, 3000 );
ADCComparatorRegionSet( ADC0_BASE, 1, 1000, 3000 );
ADCComparatorRegionSet( ADC0_BASE, 2, 1000, 3000 );
ADCComparatorRegionSet( ADC0_BASE, 3, 1000, 3000 );
ADCComparatorRegionSet( ADC0_BASE, 4, 1000, 3000 );
ADCComparatorRegionSet( ADC0_BASE, 5, 1000, 3000 );

// Enable each ADC sequence
ADCSequenceEnable( ADC0_BASE, 0 );

while( true )
{
	uint32_t intStatus = ADCComparatorIntStatus( ADC0_BASE );

	if( intStatus != 0 )
	{
		ADCComparatorIntClear(ADC0_BASE, intStatus);
	}
}

Also, while debugging this problem I found what I believe to be an error in TivaWare 2.0.1xxxx. There is an assert check if the comparator value is less than 1024 but the actual check should be against 4096. I believe this was not updated since stellaris which had 10 bit ADC's vs. the Tiva 12 bit ADC's.

void
ADCComparatorRegionSet(uint32_t ui32Base, uint32_t ui32Comp,
                       uint32_t ui32LowRef, uint32_t ui32HighRef)
{
    //
    // Check the arguments.
    //
    ASSERT((ui32Base == ADC0_BASE) || (ui32Base == ADC1_BASE));
    ASSERT(ui32Comp < 8);
    ASSERT((ui32LowRef < 1024) && (ui32LowRef <= ui32HighRef));
    ASSERT(ui32HighRef < 1024);

    //
    // Save the new region settings.
    //
    HWREG(ui32Base + ADC_O_DCCMP0 + (ui32Comp * 4)) = ((ui32HighRef << 16) |
                                                       ui32LowRef);
}

  • Hi Nathan,

    In the main code, the ADCSequenceConfigure is on ALWAYS TRIGGER Mode, but the ADC Sequencer is being enabled later. I would make a change to enable the Trigger after the ADC Sequencer is configured and Enabled.

    Also once the Digital Comparator is enabled, you may want to clear the Stale Data using ADCComparatorReset function. There is more detail in the datasheet for ADCDCRIC register.

    Also you may want to check the Errata for Digital Comparators in TM4C123 series to make sure that it is not what you are seeing.

    Amit

  • Amit,

    I'm not quite sure what you mean by enabling the trigger after configuring and enabling the sequence. Isn't the trigger set in my call to configure my sequence? I made the change to reset the comparator and now it will trigger from all events. However only the 2nd, 4th, and 6th comparator interrupts are placed in the correct bit. The 1st interrupt is in the 5th spot, the 5th is in the 1st spot etc. Here is my updated code:

    	ADCSequenceDisable( ADC0_BASE, 0 );
    
    	// Set up the ADC trigger to be from the processor (software)
    	ADCSequenceConfigure( ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0 );
    
    	// Set up the ADC interrupt steps
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 0, XOUT_CHANNEL | ADC_CTL_CMP0 );
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 1, XOUT_CHANNEL | ADC_CTL_CMP1 );
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 2, YOUT_CHANNEL | ADC_CTL_CMP2 );
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 3, YOUT_CHANNEL | ADC_CTL_CMP3 );
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 4, ZOUT_CHANNEL | ADC_CTL_CMP4 );
    	ADCSequenceStepConfigure( ADC0_BASE, 0, 5, ZOUT_CHANNEL | ADC_CTL_CMP5 | ADC_CTL_END);
    
    	// Configure the comparators
    	ADCComparatorConfigure( ADC0_BASE, 0, ADC_COMP_INT_LOW_ONCE );
    	ADCComparatorConfigure( ADC0_BASE, 1, ADC_COMP_INT_HIGH_ONCE );
    	ADCComparatorConfigure( ADC0_BASE, 2, ADC_COMP_INT_LOW_ONCE );
    	ADCComparatorConfigure( ADC0_BASE, 3, ADC_COMP_INT_HIGH_ONCE );
    	ADCComparatorConfigure( ADC0_BASE, 4, ADC_COMP_INT_LOW_ONCE );
    	ADCComparatorConfigure( ADC0_BASE, 5, ADC_COMP_INT_HIGH_ONCE );
    
    	ADCComparatorRegionSet( ADC0_BASE, 0, 1000, 3000 );
    	ADCComparatorRegionSet( ADC0_BASE, 1, 1000, 3000 );
    	ADCComparatorRegionSet( ADC0_BASE, 2, 1000, 3000 );
    	ADCComparatorRegionSet( ADC0_BASE, 3, 1000, 3000 );
    	ADCComparatorRegionSet( ADC0_BASE, 4, 1000, 3000 );
    	ADCComparatorRegionSet( ADC0_BASE, 5, 1000, 3000 );
    
    	ADCComparatorReset(ADC0_BASE, 0, true, true);
    	ADCComparatorReset(ADC0_BASE, 1, true, true);
    	ADCComparatorReset(ADC0_BASE, 2, true, true);
    	ADCComparatorReset(ADC0_BASE, 3, true, true);
    	ADCComparatorReset(ADC0_BASE, 4, true, true);
    	ADCComparatorReset(ADC0_BASE, 5, true, true);
    
    	// Enable each ADC sequence
    	ADCSequenceEnable( ADC0_BASE, 0 );
    
    	while( true )
    	{
    		uint32_t intStatus = ADCComparatorIntStatus( ADC0_BASE );
    
    		if( intStatus != 0 )
    		{
    			ADCComparatorIntClear(ADC0_BASE, intStatus);
    		}
    	}

  • Hi Nathan

    I was referring to the API Call for enabling the trigger.

    // Set up the ADC trigger to be from the processor (software)
    ADCSequenceConfigure( ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0 );

    This has to be done after the Sequencer is Enabled.

    However the issue at hand is when ZOUT is LOW, the Interrupt for Digital Comparator XOUT when LOW is getting set. What about Interrupt for 3rd digital comparator. Does it fire correctly?

    Amit

  • I think i may be seeing the same problem.  I'm trying to use 4 channels and 8 comparators in a single sequence to look for low and high conditions.  

    If I use a single channel with two comparators(two steps) in a single sequence I get the results I expects.  I connect the channel to a voltage supply and can consistently cause high and low interrupts.  

    As soon as I try to add any additional channels the behavior becomes strange.  If I add two additional steps using two different comparators(same sequence or even a different sequence) on a different channel I lose the expected behavior on both channels.  If I ground my second channel my first channel will no longer see my high or low conditions.  If I tie both channels to my voltage source I can then, once again, cause my high and low conditions to trigger.  If I leave one pin floating and the other tied to my voltage source I can once again cause my high and low interrupts but only at a voltage higher than the voltage I programmed the comparators to trigger on.  

    It almost seems as if there is some averaging between channels.  The more channels I add the higher the threshold to trigger my high comparator becomes.  I need to monitor 4 channels separately for both high and lows.  Is there something that would cause this behavior?

  • For the record I was never able to find a solution to this issue. We ended up changing the design to work around the issue.

  • Looks like my Launchpad has Channel 3 and Channel 0 shorted somehow(PE0 and PE3).  I decided to use channels 1 & 0(PE2 & PE3) but mistakenly setup PE3 and PE0.  It worked as I intended despite setting up the wrong pin.  After fixing this mistake it still worked.  Looks like PE0 and PE3 are acting as one.  Not sure if it's a flaw in the launchpad in general or just my board.  I programmed our own board which contains a Tiva(slightly different version) and it works correctly.

    I did also running into the Digital comparator mistake but that was mentioned in the erratta.  Not explained well but easy to work around.

  • I am having exactly the same issues with my Tiva TM4c123GH6PM. I have configured all 12 Analog Input Channels. The first 4 analog inputs are fed to SS0 of ADC0 and remaining 8 inputs are fed to SS0 of ADC1. And then they go to their respective Digital Comparators.

    Strangely, the Analog Pins are somehow coupled internally. When I change voltage levels on one analog pin, I get interrupts on several pins. I can't find any solution to this problem in the Errata.

    I have discovered another unexpected behavior. I am also using PWM on PB6 and PB7. When I start the PWM, The Digital Comparator linked to PD0 starts giving me interrupts every now and then. I used a multimeter to check the voltage and it showed ~ 3V after apprpx. every 1 sec.
    My PWM frequency is 10 KHz btw. There are all sorts of strange things going on with my launchpad. Can anybody help?

  • Hello Zaigam,

    There is a 0Ohm Resistor on the board (R9) which connected PB6 to PD0. You would need to remove the same.

    Tim, All

    I checked the board and there is no short or low impedance path between PE0 and PE3.

    Regards

    Amit

  • I found this very useful. I set my system up for interrupts. The setup code and interrupt code follows.

    I hope it is of use to someone else.


    /** ***************************************************************************
    * EnableLowVoltageAlarm
    *
    * This routine configures ADC1 to monitor APS and BAT and alarm if out of range
    *
    * The ADC runs continuously, sampling the two voltages. It then sends the
    * reading to the Digital Comparator module that classifies the ADC value
    * into one of three groups: Low Band, Mid Band or High Band.
    *
    * We are want a single interrupt for each transition from High Band, which is
    * set to our normal minimum working value - 10V - to the low band - 8 Volts.
    * Hysteresis requires that voltage move from <8 to >10 and back <8 before
    * another interrupt is triggered.
    *
    * The interrupt handler is responsible for managing the low voltage situation
    *
    * @param [in] ADCBase - Address of the ADC block
    * @param [in] HWAvg - number of readings to average
    * @param [in] IntSvc - void* Interrupt handler
    *
    **************************************************************************** */
    void MonitorPower::EnableLowVoltageAlarm( uint32_t ADCAddr, uint8_t HwAvg )
    {
    DCAddress = ADCAddr;
    // // Set up ADC Clocks
    // ADCClockConfigSet( ADC0_BASE, ADC_CLOCK_SRC_PIOSC, ADC_CLOCK_RATE_EIGHTH );
    //
    // Enable the first sample sequencer to capture the value of channel 1 when
    // the processor trigger occurs.
    // We are using three samples, so we select sequencer 1 which handles 4 ch.
    // We set this to lower priority than the main service timer function
    ADCIntDisable(ADCAddr, 0); // Disable ADC ints
    ADCIntDisable(ADCAddr, 1);
    ADCIntDisable(ADCAddr, 2);
    ADCIntDisable(ADCAddr, 3);
    ADCComparatorIntDisable(ADCAddr, 0);
    ADCComparatorIntDisable(ADCAddr, 1);
    ADCComparatorIntDisable(ADCAddr, 2);
    ADCComparatorIntDisable(ADCAddr, 3);

    // Make sure the sequencers ar off.
    ADCSequenceDisable( ADCAddr, 0 );
    ADCSequenceDisable( ADCAddr, 1 );
    ADCSequenceDisable( ADCAddr, 2 );
    ADCSequenceDisable( ADCAddr, 3 );


    ADCSequenceStepConfigure(ADCAddr, 0, 0, ADC_CTL_CH1 // APS V
    | ADC_CTL_CMP0 );
    ADCSequenceStepConfigure(ADCAddr, 0, 1, ADC_CTL_CH3 // Bat V
    | ADC_CTL_CMP1 );
    ADCSequenceStepConfigure(ADCAddr, 0, 2, ADC_CTL_CH4 // VOut
    | ADC_CTL_CMP2 );
    ADCSequenceStepConfigure(ADCAddr, 0, 3, ADC_CTL_CH4 // Dummy
    | ADC_CTL_CMP3
    | ADC_CTL_END );

    ADCHardwareOversampleConfigure( ADCAddr, HwAvg );

    // Now set up the digital comparators
    ADCComparatorConfigure( ADCAddr, 0, ADC_COMP_INT_LOW_HONCE ); // APS V
    ADCComparatorConfigure( ADCAddr, 1, ADC_COMP_INT_LOW_HONCE ); // Bat V
    ADCComparatorConfigure( ADCAddr, 2, ADC_COMP_INT_LOW_HONCE ); // Vout
    ADCComparatorConfigure( ADCAddr, 3, ADC_COMP_INT_LOW_HONCE ); // dummy

    // Set the inital comparator values.
    ADCComparatorRegionSet( ADCAddr, 0, V2Cnt( 8 ), V2Cnt( 10 )); // APS
    ADCComparatorRegionSet( ADCAddr, 1, V2Cnt( 8 ), V2Cnt( 10 )); // APS
    ADCComparatorRegionSet( ADCAddr, 2, V2Cnt( 8 ), V2Cnt( 10 )); // Bat
    ADCComparatorRegionSet( ADCAddr, 3, V2Cnt( 8 ), V2Cnt( 10 )); // dummy

    // Clear stale data
    ADCComparatorReset( ADCAddr, 0, 1, 1 );
    ADCComparatorReset( ADCAddr, 1, 1, 1 );
    ADCComparatorReset( ADCAddr, 2, 1, 1 );
    ADCComparatorReset( ADCAddr, 3, 1, 1 );

    // Set up interrupts
    // Enable the Low Voltage fault interrupts..
    ADCIntClear(ADCAddr, 0); // Clear any interrupts
    IntPrioritySet( INT_ADC1SS0, 2<<5 ); // Set high priority
    ADCIntEnable(ADCAddr, 0); // Enable LV fault interrupt
    ADCIntEnableEx(ADCAddr, ADC_INT_DCON_SS0);
    ADCComparatorIntEnable( ADCAddr, 0);
    IntEnable( INT_ADC1SS0_TM4C123 ); // Turn on in NVIC

    // Start up the sequence to loop forever.
    // Since only one sequence on this ADC, set for high priority
    ADCSequenceEnable(ADCAddr, 0); // Start.. Auto-repeat forever
    ADCSequenceConfigure(ADCAddr, 0, ADC_TRIGGER_ALWAYS, 0);

    }

    /** ***************************************************************************
    * LVAlarmService
    *
    * This ISR handles the low volage alarms generated by the digital comparator.
    *
    **************************************************************************** */

    // Silicon Revisions 6 & 7 have bug in DCComparator interrupt. ADC#04
    // The bits are shifted by 1.
    // If they fix this bug, the following defines must change:

    #define APS_LV_FAULT 2
    #define BAT_LV_FAULT 4
    #define VOUT_LV_FAULT 8


    void LVAlarmHandler(void)
    {
    uint32_t DCStatus;
    long xHigherPriorityTaskWoken = false;

    DCStatus = ADCComparatorIntStatus( ADC1_BASE );

    //
    // Clear all the ADC Digital Comparator interrupts that are set
    //
    ADCComparatorIntClear( ADC1_BASE, DCStatus );

    // check for APS disconnect
    if ( DCStatus & APS_LV_FAULT )
    {
    // If APS fails, turn on Battery
    EnBattPower.Set();
    }

    // check for Battery disconnect
    if ( DCStatus & BAT_LV_FAULT )
    {
    // If Batt fails, turn on APS
    EnBattPower.Set();
    }


    // Report the alarm.. FREERTOS notify..
    if ( Status & (APS_LV_FAULT | BAT_LV_FAULT) )
    {
    BattCharger.NotifyFromISR( EmergencyStop, &xHigherPriorityTaskWoken );
    }

    if( xHigherPriorityTaskWoken == pdTRUE)
    {
    portYIELD_FROM_ISR(true);
    }

    }
  • Hello Dan,

    Couple of suggestions on top of the awesome code

    1. An example usage of MonitorPower::EnableLowVoltageAlarm being called to show the example
    2. Some other functions LVAlarmHandler are not defined. Would be useful to elaborate the same.

    What I did like was the level of comments.

    Regards
    Amit
  • I call this the setup routine from inside of my ADC handler thread.  The pins are already configured with the TIVA Pin tool.  

    The call to enable is:

    // Set up the Low Voltage Alarm processor.

    EnableLowVoltageAlarm( ADC1_BASE, 32 );

    I am using ADC0 to read voltages on 8 different channels.  I wanted the power fail interrupts to be very quick, but not respond to spikes.  Since ADC1 was idle, I use that for the power monitor.

    The second parameter to EnableLowVoltageAlarm controls the hardware averaging.   I selected 32 as I am monitoring 3 channels, as well as a dummy channel as required by Errata ADC#04.  So a basic conversion takes 32*4 micro seconds. That lets me respond in 128 us, but still filters out the occasional noise glitch that may occur on the power rails.  

    The event handler comes from product code and could be simplified.   Some of the functions are from FreeRTOS.  The following code adds more comments.  Sorry about the lack of indentation.

    /** ***************************************************************************
    * LVAlarmService
    *
    * This ISR handles the low volage alarms generated by the digital comparator.
    *
    **************************************************************************** */

    // Silicon Revisions 6 & 7 have bug in DCComparator interrupt. ADC#04
    // The bits are shifted by 1.
    // If they fix this bug, the following defines must change:

    #define APS_LV_FAULT 2
    #define BAT_LV_FAULT 4
    #define VOUT_LV_FAULT 8


    void LVAlarmHandler(void)
    {
    uint32_t DCStatus;
    SourceEvents_t Event;

    // Flag used by FreeRTOS to control IRQ Chaining..
    // Not relavent to basic operation
    long xHigherPriorityTaskWoken = false;

    // Get the cause into DCStatus.
    // Errata ADC#04 says events are shifted.
    // The defines handle the shift for now.
    DCStatus = ADCComparatorIntStatus( ADC1_BASE );

    //
    // Clear all the ADC Digital Comparator interrupts that are set
    //
    ADCComparatorIntClear( ADC1_BASE, DCStatus );

    // check for APS disconnect
    if ( DCStatus & APS_LV_FAULT )
    {
    // Application processing of low power
    // If APS fails, turn on Battery immediately.
    // use PIN object which abstracts GPIO

    EnBattPower.Set();
    Event = SourceRemovedFromAps;

    // FreeRTOS call to send a message to the handler.
    xQueueSendToFrontFromISR ( EventQue,
    &Event,
    &xHigherPriorityTaskWoken);
    }

    // check for Battery disconnect
    if ( DCStatus & BAT_LV_FAULT )
    {
    // If Batt fails, turn on APS
    EnBattPower.Set();
    Event = SourceRemovedFromBat;
    xQueueSendToFrontFromISR ( EventQue,
    &Event,
    &xHigherPriorityTaskWoken);
    }

    // FreeRTOS method of IRQ chaining.
    if( xHigherPriorityTaskWoken == pdTRUE)
    {
    portYIELD_FROM_ISR(true);
    }
    }

  • Hello Dan,

    Excellent work (especially) on letting know it is FreeRTOS and not any of the other RTOS's.

    Regards
    Amit
  • Make this Vote #2 for presence of comments & special mention for being kind/considerate enough to, "Assist others."