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.
Any help would be much appreciated. Thank you in advance