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 digital comparator interrupt automatically interrupts on initialization

Hi all,

I'm making an oscilloscope which requires triggering on rising and/or falling edges.

I'm using 2 sequencer steps to handle rising and falling edges, where the interrupt mode is Hysteresis Once, so basically I want trigger when the signal goes from high region to low region, and vice versa.

This works fine except... on initialization, if the input voltage is in the high region or the low region, a trigger will occur. But I don't want this because I only want triggers when the voltage makes a transition from high to middle to low region and vice versa.

For instance, in the code (attached) I have set the high region to be 3000 and low region to be 1000. Now if I only enable the Hysteresis Once, Low Band interrupt (trigger when going from high to low), and on start up the voltage is say... 0V, the trigger will occur, but I don't want this. It should interrupt when it goes from above 3000 to below 1000.

In the code below, the function

void sampling_start(uint16_t numSamples, uint32_t clockFreq,
		uint32_t samplingFreq, uint8_t channel);

is being called to initialize sampling where samples are transferred to memory using uDMA. When a trigger occurs, the ADC sequencer, DMA, and timers will be disabled until sampling_start is called again.

This is my code for initializing ADC sampling:

#include <stdint.h>
#include <stdbool.h>

/* Tivaware Header files */
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_adc.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/adc.h"
#include "driverlib/udma.h"

/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

/*    FOR DEBUG -------------------------------- */
#include <ti/drivers/GPIO.h>
#include "Board.h"

#include "adc.h"

// Number of transfers required to obtain desired number of samples
static uint8_t ui8DMATransfersPerBufA;
static uint8_t ui8DMATransfersPerBufB;

uint16_t pui16SamplesA[MAX_NUM_SAMPLES + DMA_TRANSFER_SIZE];
uint16_t pui16SamplesB[MAX_NUM_SAMPLES + DMA_TRANSFER_SIZE];
// DMA transfer destinations
uint16_t *pui16DMADestA; // = pui16SamplesA + 2;
uint16_t *pui16DMADestB; // = pui16SamplesB + 2;

// Comparator levels: [channel][region]
static uint16_t pui16ComparatorLevels[2][2];

static volatile uint32_t uDMATransferCount = 0;

static volatile uint16_t ui16SamplesATracker;
uint16_t ui16TriggerPointA;
static volatile uint8_t ui8TriggeredA;
uint8_t ui8DoneA;
static volatile uint16_t ui16SamplesTakenAfterTriggerA;

uint8_t pui8DMAControlTable[1024];

static void advance_dma_dest(uint8_t channel) {
	switch (channel) {
	case CHAN_A:
		pui16DMADestA += DMA_TRANSFER_SIZE;
		if (pui16DMADestA
				>= (pui16SamplesA + (DMA_TRANSFER_SIZE * ui8DMATransfersPerBufA))) {
			pui16DMADestA = pui16SamplesA;
		}
		break;
	case CHAN_B:
		break;
	}
}

uint32_t foo = 0;
void adc_timer_irq(void) {
	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

	// Doing this will cause stack overflow, only do this for showing 1 MHz
//	if (foo++ % 2) {
//		GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_PIN_4);
//	} else {
//		GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, 0);
//	}
//	GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_PIN_4);
//	GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_4, 0);

	ui16SamplesATracker++;
	ui16SamplesATracker %= (DMA_TRANSFER_SIZE * ui8DMATransfersPerBufA);

	if (ui8TriggeredA) {
		ui16SamplesTakenAfterTriggerA++;
	}
}

uint32_t i = 0;
void adc_A_comp_irq(void) {
	ADCComparatorIntClear(ADC0_BASE, ADC_DCISC_DCINT0 | ADC_DCISC_DCINT1);

	foobar = *(uint16_t *) (ADC0_BASE + ADC_O_SSFIFO0);

	ui16TriggerPointA = ui16SamplesATracker;
	ui8TriggeredA = 1;

	// Don't trigger again until next set of sampling begins
	IntDisable(INT_ADC0SS1);
}

void adc_B_comp_irq(void) {
	// TODO: ADC1 may have its own set of digital comparators, so check
	// if we can use DCINT0 and DCINT1
	ADCComparatorIntClear(ADC1_BASE, ADC_DCISC_DCINT2 | ADC_DCISC_DCINT3);
}

void adc_A_dma_irq(void) {

	uint32_t modePrimary;
	uint32_t modeAlternate;

	ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0);

	// Get mode statuses
	modePrimary = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
	modeAlternate = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);

	// Reload the control structures
	if ((modePrimary == UDMA_MODE_STOP) && (modeAlternate != UDMA_MODE_STOP)) {
		// Need to reload primary control structure
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
		UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0 + 1),
				pui16DMADestA,
				DMA_TRANSFER_SIZE);
		uDMATransferCount++;
		advance_dma_dest(CHAN_A);

	} else if ((modePrimary != UDMA_MODE_STOP)
			&& (modeAlternate == UDMA_MODE_STOP)) {
		// Need to reload alternate control structure
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
		UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0 + 1),
				pui16DMADestA,
				DMA_TRANSFER_SIZE);
		uDMATransferCount++;
		advance_dma_dest(CHAN_A);

	} else {
		// Either both still not stopped, or both stopped. This is an error
#ifdef DEBUG
		System_printf("Error in uDMA control structure modes\n");
		System_printf("uDMATransferCount: %d\n", uDMATransferCount);
		System_printf("Primary mode: %d\nAlternate mode: %d\n", modePrimary,
				modeAlternate);
		System_printf(
				"For reference: Stop:%d, Basic:%d, Auto:%d, PingPong:%d, MemScatter:%d, PerScatter:%d\n",
				UDMA_MODE_STOP, UDMA_MODE_BASIC, UDMA_MODE_AUTO,
				UDMA_MODE_PINGPONG, UDMA_MODE_MEM_SCATTER_GATHER,
				UDMA_MODE_PER_SCATTER_GATHER);
		System_flush();
#endif
	}

	// Stop udma if we have triggered, and obtained the correct amount of samples
	if (ui16SamplesTakenAfterTriggerA
			>= (DMA_TRANSFER_SIZE * ui8DMATransfersPerBufA / 2)) {
		sampling_stop(CHAN_A);
		ui8DoneA = 1;
	}
}

void adc_B_dma_irq(void) {
	ADCIntClearEx(ADC1_BASE, ADC_INT_DMA_SS0);
}

/*
 * ========== adc_trigger_timer_init ==========
 * @brief	Initializes the timer for triggering ADC samples
 */
void adc_trigger_timer_init(uint32_t ui32ClockFreq, uint32_t ui32SampleFreq) {

	// Enable timer peripheral clock
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER1)) {
	}

	// Register interrupt
	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
	IntRegister(INT_TIMER1A, adc_timer_irq);
	IntEnable(INT_TIMER1A);

	TimerConfigure(TIMER1_BASE, TIMER_CFG_A_PERIODIC_UP);
	TimerLoadSet(TIMER1_BASE, TIMER_A, (ui32ClockFreq / ui32SampleFreq));
	TimerControlTrigger(TIMER1_BASE, TIMER_A, true);
	TimerADCEventSet(TIMER1_BASE, TIMER_ADC_TIMEOUT_A);
	TimerEnable(TIMER1_BASE, TIMER_A);
}

void comparator_levels_set(uint16_t low, uint16_t high, uint8_t channel) {

	pui16ComparatorLevels[channel][LOW] = low;
	pui16ComparatorLevels[channel][HIGH] = high;

	switch (channel) {
	case CHAN_A:

		ADCIntDisableEx(ADC0_BASE, ADC_INT_DCON_SS1);
		ADCSequenceDisable(ADC0_BASE, 1);

		ADCSequenceStepConfigure(ADC0_BASE, 1, 0,
		ADC_CTL_CH8 | ADC_CTL_CMP0 | ADC_CTL_IE);
		ADCSequenceStepConfigure(ADC0_BASE, 1, 1,
		ADC_CTL_CH8 | ADC_CTL_CMP1 | ADC_CTL_END | ADC_CTL_IE);

		ADCReferenceSet(ADC0_BASE, ADC_REF_INT);

		ADCComparatorConfigure(ADC0_BASE, 0, ADC_COMP_INT_LOW_HONCE);
		ADCComparatorConfigure(ADC0_BASE, 1, ADC_COMP_INT_HIGH_HONCE);
		ADCComparatorRegionSet(ADC0_BASE, 0, low, high);
		ADCComparatorRegionSet(ADC0_BASE, 1, low, high);

		ADCComparatorReset(ADC0_BASE, 0, true, true);

		ADCIntEnableEx(ADC0_BASE, ADC_INT_DCON_SS1);

		IntRegister(INT_ADC0SS1, adc_A_comp_irq);

		IntEnable(INT_ADC0SS1);

		ADCSequenceEnable(ADC0_BASE, 1);

		ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_TIMER, 1);

		break;
	case CHAN_B:
		ADCComparatorConfigure(ADC1_BASE, 2, ADC_COMP_INT_LOW_HONCE);
		ADCComparatorConfigure(ADC1_BASE, 3, ADC_COMP_INT_HIGH_HONCE);
		ADCComparatorRegionSet(ADC1_BASE, 2, low, high);
		ADCComparatorRegionSet(ADC1_BASE, 3, low, high);
		break;
	}
}

uint16_t comparator_level_get(uint8_t region, uint8_t channel) {
	return pui16ComparatorLevels[channel][region];
}

/*
 * ========== adc_init ==========
 * @brief	Initializes ADCs for both channels A and B
 */
void adc_init(void) {

	/*
	 * Channel A:
	 * ADC0
	 * Sequencer 0, Step 0 - Sample and uDMA
	 * Sequencer 1, Step 0 - Falling edge trigger (CMP0)
	 * Sequencer 1, Step 1 - Rising edge trigger (CMP1)
	 * Pin PE5 (Channel A8)
	 *
	 * Channel B:
	 * ADC1
	 * Sequencer 0, Step 0 - Sample and uDMA
	 * Sequencer 1, Step 0 - Falling edge trigger (CMP2)
	 * Sequencer 1, Step 1 - Rising edge trigger (CMP3)
	 * Pin PE4 (Channel A9)
	 */

	// Enable GPIO peripheral clock for the ADC input
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	SysCtlPeripheralReset(SYSCTL_PERIPH_GPIOE);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)) {
	}
	// Enable ADC peripheral clock
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)) {
	}
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
	SysCtlPeripheralReset(SYSCTL_PERIPH_ADC1);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1)) {
	}

	// Configure GPIO pins as ADC inputs
	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_4);
	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5);

	// Disable interrupts
	ADCIntDisableEx(ADC0_BASE, ADC_INT_DMA_SS0); // | ADC_INT_DCON_SS1);
	ADCIntDisableEx(ADC1_BASE, ADC_INT_DMA_SS0);	// | ADC_INT_DCON_SS1);

	// Disable sequences
	ADCSequenceDisable(ADC0_BASE, 0);
//	ADCSequenceDisable(ADC0_BASE, 1);
	ADCSequenceDisable(ADC1_BASE, 0);
//	ADCSequenceDisable(ADC1_BASE, 1);

	// Configure the steps of each sequencer
	// Channel A
	ADCSequenceStepConfigure(ADC0_BASE, 0, 0,
	ADC_CTL_CH8 | ADC_CTL_END | ADC_CTL_IE);
//	ADCSequenceStepConfigure(ADC0_BASE, 1, 0,
//	ADC_CTL_CH8 | ADC_CTL_CMP0 | ADC_CTL_IE);
//	ADCSequenceStepConfigure(ADC0_BASE, 1, 1,
//	ADC_CTL_CH8 | ADC_CTL_CMP1 | ADC_CTL_END | ADC_CTL_IE);
	// Channel B
	ADCSequenceStepConfigure(ADC1_BASE, 0, 0,
	ADC_CTL_CH9 | ADC_CTL_END | ADC_CTL_IE);
//	ADCSequenceStepConfigure(ADC1_BASE, 1, 0,
//	ADC_CTL_CH9 | ADC_CTL_CMP2 | ADC_CTL_IE);
//	ADCSequenceStepConfigure(ADC1_BASE, 1, 1,
//	ADC_CTL_CH9 | ADC_CTL_CMP3 | ADC_CTL_END | ADC_CTL_IE);

	// Configure to use internal 3V reference
//	ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
	ADCReferenceSet(ADC1_BASE, ADC_REF_INT);

	// Configure ADC comparators
//	comparator_levels_set(COMP_DEFAULT_LOW, COMP_DEFAULT_HIGH, CHAN_A);
//	comparator_levels_set(COMP_DEFAULT_LOW, COMP_DEFAULT_HIGH, CHAN_B);

//	ADCComparatorReset(ADC0_BASE, 0, true, true);
//	ADCComparatorReset(ADC0_BASE, 1, true, true);

	// Enable ADC interrupts for uDMA transfer completion,
	// and digital comparator
	ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0); // | ADC_INT_DCON_SS1);
	ADCIntEnableEx(ADC1_BASE, ADC_INT_DMA_SS0);	// | ADC_INT_DCON_SS1);

	// Register interrupts
//	IntRegister(INT_ADC0SS1, adc_A_comp_irq);
//	IntRegister(INT_ADC1SS1, adc_B_comp_irq);
	IntRegister(INT_ADC0SS0, adc_A_dma_irq);
	IntRegister(INT_ADC1SS0, adc_B_dma_irq);

	// Enable interrupts
	IntEnable(INT_ADC0SS0);
//	IntEnable(INT_ADC0SS1);
	IntEnable(INT_ADC1SS0);
//	IntEnable(INT_ADC1SS1);

	// Enable ADC to use uDMA
	ADCSequenceDMAEnable(ADC0_BASE, 0);
	ADCSequenceDMAEnable(ADC1_BASE, 0);

	// Enable ADC sequencers
	ADCSequenceEnable(ADC0_BASE, 0);
//	ADCSequenceEnable(ADC0_BASE, 1);
	ADCSequenceEnable(ADC1_BASE, 0);
//	ADCSequenceEnable(ADC1_BASE, 1);

	// Configure the trigger sources and priorities
	// All sequencers are set to be triggered by timer
	// Sequence 0 has higher priorty (priority 0) than sequence 1 (priority 1)
	ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);
//	ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_TIMER, 1);
	ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_TIMER, 0);
//	ADCSequenceConfigure(ADC1_BASE, 1, ADC_TRIGGER_TIMER, 1);
}

/*
 * @params	Number of transfers required to obtain requires number of samples
 */
void dma_start(uint8_t ui8NumTransfers, uint8_t channel) {

	switch (channel) {
	case CHAN_A:
		// Disable before configure
		uDMAChannelDisable(UDMA_CHANNEL_ADC0);

		// Set the number of transfers
		ui8DMATransfersPerBufA = ui8NumTransfers;

		// Reset the destination pointer to start of buffer
		pui16DMADestA = pui16SamplesA;

		// Configure the transfer parameters
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
		UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA,
		DMA_TRANSFER_SIZE);
		advance_dma_dest(CHAN_A);
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
		UDMA_MODE_PINGPONG, (void *) (ADC0_BASE + ADC_O_SSFIFO0), pui16DMADestA,
		DMA_TRANSFER_SIZE);
		advance_dma_dest(CHAN_A);

		uDMAChannelEnable(UDMA_CHANNEL_ADC0);
		break;

	case CHAN_B:
		// Disable before configure
		uDMAChannelDisable(UDMA_CHANNEL_ADC1);

		// Reset the destination pointer to start of buffer

		// TODO channel B

		uDMAChannelEnable(UDMA_CHANNEL_ADC1);
		break;
	}
}

void dma_init(void) {

	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)) {
	}

	// Enable uDMA
	uDMAEnable();
	// Set the control table for uDMA
	uDMAControlBaseSet(&pui8DMAControlTable[0]);

	// Disable unneeded attributes of the uDMA channels
	uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL);
	uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC1, UDMA_ATTR_ALL);

	// Channel A
	uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
	UDMA_SIZE_16 |
	UDMA_SRC_INC_NONE |
	UDMA_DST_INC_16 |
	UDMA_ARB_1);
	uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
	UDMA_SIZE_16 |
	UDMA_SRC_INC_NONE |
	UDMA_DST_INC_16 |
	UDMA_ARB_1);
}

void sampling_start(uint16_t numSamples, uint32_t clockFreq,
		uint32_t samplingFreq, uint8_t channel) {

	ui16SamplesATracker = 0;
	ui16TriggerPointA = 0;
	ui8TriggeredA = 0;
	ui8DoneA = 0;
	ui16SamplesTakenAfterTriggerA = 0;

//	//
//	// Debug pins
//	//
//	// Enable Port C
//	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
//	while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOC)) {
//	}
//	// Set C4 to be output
//	GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_4);

	dma_init();
	dma_start((numSamples / DMA_TRANSFER_SIZE), channel);
	adc_init();
	comparator_levels_set(COMP_DEFAULT_LOW, COMP_DEFAULT_HIGH, CHAN_A);
	adc_trigger_timer_init(clockFreq, samplingFreq);

}

void sampling_stop(uint8_t channel) {
	switch (channel) {
	case CHAN_A:
		IntDisable(INT_ADC0SS0);
		uDMAChannelDisable(UDMA_CHANNEL_ADC0);
//		TimerDisable(TIMER1_BASE, TIMER_A);
		ADCSequenceDisable(ADC0_BASE, 0);
		break;
	case CHAN_B:
		break;
	}
}

I've also attached my project for your viewing if necessary.

1832.Digiscope.rar

Any help would be much appreciated. Thank you in advance