Interrupt from ADC_ETC keeps spamming the interrupt even though I'm clearing it.

frankzappa

Well-known member
Hello!

I've set up ADC_ETC to read 10 sensors in a sequence and I'm having trouble with the interrupt I'm trying to use to move the sensor data after ADC_ETC completes. Can anyone see what's going on? I'm using the Done0 interrupt and I think I'm clearing it properly but not sure if that's the correct approach. I would appreciate some help, I'm seriously getting depressed here, been trying to get this to work for three weeks. The problem is that the interrupt is firing constantly. If I remove the print I get the correct values but clearly it should just print once per second since the ADC frequency is 1Hz.

Code:
//  ADC_ETC works but interrupts keep firing

#include "Arduino.h"
#include "ADC.h"

// ADC settings
ADC *adc = new ADC();
float frequency = 1.0f;
// Buffer size and number of buffers
#define BUFFER_SIZE 10 // Size of each buffer (5 sensors per ADC)
#define NUM_BUFFERS 3  // Number of buffers to cycle through
// Define the buffers as a 2D array
DMAMEM __attribute__((aligned(32))) static uint16_t buffers[NUM_BUFFERS][BUFFER_SIZE];
// Index variable to keep track of the current buffer being filled
volatile int buffer_index = 0;
// Index of the last filled buffer
volatile int last_filled_buffer_index = 0;
// Flag to indicate new data availability
volatile bool new_data_available = false;
// Pointer to the current buffer being filled by DMA
volatile uint16_t* current_buffer = buffers[0];
extern "C" {
    extern void xbar_connect(unsigned int input, unsigned int output);
}
// Forward declarations
void adc_etc_reset();
void adc_etc_setup();
void adc_init();
void setup_timer_and_xbar(const float frequency);
void start_timer();
void dma_ch0_isr();
void setup() {
    while (!Serial && millis() < 5000);
    Serial.println("Setup ADC with Software Trigger");
    // Configure ADC1 and ADC2
    adc->adc0->setAveraging(4);
    adc->adc0->setResolution(12);
    adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
    adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
    adc->adc0->singleMode();
    adc->adc0->disableInterrupts();
    adc->adc1->setAveraging(4);
    adc->adc1->setResolution(12);
    adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
    adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
    adc->adc1->singleMode();
    adc->adc1->disableInterrupts();
    adc_init();
    adc_etc_reset();  // Reset the ADC_ETC module
    adc_etc_setup();  // Set up the ADC_ETC for software triggering
    setup_timer_and_xbar(frequency);
    delay(1000);
    start_timer();
}
void loop() {
    delay(500);
    Serial.println("Values:");
    // Print the values from the last filled buffer
    for (int i = 0; i < BUFFER_SIZE; i++) {
        Serial.print(buffers[0][i]);
        if (i < BUFFER_SIZE - 1) {
            Serial.print(", ");
        }
       
    }
    Serial.println();
    // Print the values from the last filled buffer
    for (int i = 0; i < BUFFER_SIZE; i++) {
        buffers[0][i] = 0;
        buffers[1][i] = 0;
        buffers[2][i] = 0;
    }


    // Print 5 values from TRIG[0]
volatile uint32_t* trig0_results = &IMXRT_ADC_ETC.TRIG[0].RESULT_1_0; // Get the address of the first result
for (int i = 0; i < 5; i++) {
    uint16_t value = *((volatile uint16_t*)trig0_results + i); // Access the 16-bit values sequentially
    Serial.print(value);
   
    Serial.print(", ");
}
// Print 5 values from TRIG[4]
volatile uint32_t* trig4_results = &IMXRT_ADC_ETC.TRIG[4].RESULT_1_0; // Get the address of the first result
for (int i = 0; i < 5; i++) {
    uint16_t value = *((volatile uint16_t*)trig4_results + i); // Access the 16-bit values sequentially
    Serial.print(value);
    if (i < 4) {
        Serial.print(", ");
    }
}
Serial.println();
}


void dma_ch0_isr() {
    Serial.println("DMA ISR triggered");
   
    __disable_irq(); // Disable interrupts at the start of the ISR
   
    // Read 5 values from TRIG[0] results
    volatile uint32_t* trig0_results = &IMXRT_ADC_ETC.TRIG[0].RESULT_1_0;
    for (int i = 0; i < 5; i++) {
        current_buffer[i] = *((volatile uint16_t*)trig0_results + i);
    }
    // Read 5 values from TRIG[4] results
    volatile uint32_t* trig4_results = &IMXRT_ADC_ETC.TRIG[4].RESULT_1_0;
    for (int i = 0; i < 5; i++) {
        current_buffer[5 + i] = *((volatile uint16_t*)trig4_results + i);
    }
    // Update the index of the last filled buffer
    last_filled_buffer_index = buffer_index;
    // Update buffer index and wrap around if necessary
    buffer_index = (buffer_index + 1) % NUM_BUFFERS;
    // Update the current buffer pointer to the next buffer
    current_buffer = buffers[buffer_index];
    // Set flag to indicate new data is available
    new_data_available = true;
    ADC_ETC_DONE0_1_IRQ |= ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);   // clear
    asm("dsb");
   
       
    __enable_irq(); // Re-enable interrupts before exiting the ISR
}



void adc_init() {
    ADC1_CFG |= ADC_CFG_ADTRG; // Hardware trigger
    ADC1_HC0 = 16; // ADC_ETC channel, 144 int enabled, 16 int disabled
    ADC1_GC &= ~ADC_GC_ADCO; // Continuous conversion disabled, same as adc->adcX->singleMode()
    ADC2_CFG |= ADC_CFG_ADTRG; // Hardware trigger
    ADC2_HC0 = 16; // ADC_ETC channel
    ADC2_GC &= ~ADC_GC_ADCO; // Continuous conversion disabled
}
void adc_etc_reset() {
    IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // Perform a software reset on ADC_ETC
    asm volatile("dsb"); // Ensure SOFTRST operation is completed
    IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // Clear SOFTRST
    delay(5);
}
void adc_etc_setup() {
    const uint8_t chainLength0 = 5;  // Chain length for TRIG[0]
    const uint8_t chainLength4 = 5;  // Chain length for TRIG[4]
    // Clear the TSC_BYPASS bit to ensure ADC2 is not occupied by the Touch Screen Controller
    IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_TSC_BYPASS; // Clear TSC_BYPASS (bit 30)
    // Enable TRIG[0] for xbar triggering
    IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_TRIG_ENABLE((1 << 0));
   
    // Configure TRIG[0] for ADC1
    IMXRT_ADC_ETC.TRIG[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength0 - 1);
    // Configure chain for TRIG[0]
    IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(7) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(8) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(1);
    IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(12) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(11) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(1);
    IMXRT_ADC_ETC.TRIG[0].CHAIN_5_4 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_IE0(1) |
        ADC_ETC_TRIG_CHAIN_CSEL0(6) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1);
    // Configure TRIG[4] for ADC2 in sync mode with TRIG[0]
    IMXRT_ADC_ETC.TRIG[4].CTRL =
        ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength4 - 1) |
        ADC_ETC_TRIG_CTRL_SYNC_MODE;
    // Configure chain for TRIG[4]
    IMXRT_ADC_ETC.TRIG[4].CHAIN_1_0 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(5) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(15) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(5);
    IMXRT_ADC_ETC.TRIG[4].CHAIN_3_2 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(0) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(5) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(13) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(5);
    IMXRT_ADC_ETC.TRIG[4].CHAIN_5_4 =
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_CSEL0(14) |
        ADC_ETC_TRIG_CHAIN_HWTS0(5);
        // Attach the interrupt vector for ADC_ETC TRIG[4] interrupt
    attachInterruptVector(IRQ_ADC_ETC0, dma_ch0_isr);
    // Set interrupt priority if needed (optional, lower number = higher priority)
    NVIC_SET_PRIORITY(IRQ_ADC_ETC0, 10);  // Example priority level
    // Enable the ADC_ETC interrupt
    NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
   
}

void setup_timer_and_xbar(const float frequency) {
    CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // Enable clock for XBAR
    // Connect TMR4 Timer output to ADC_ETC Trigger input
    xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00); // Connect Timer to trigger TRIG[0]
}
void start_timer() {
    // Configure TMR4 Timer directly
    TMR4_ENBL &= ~(1 << 0); // Disable Timer
    TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
    TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
    TMR4_CNTR0 = 0;
    TMR4_LOAD0 = 0;
    TMR4_COMP10 = (float)F_BUS_ACTUAL / frequency / 2; // Set compare value based on desired frequency
    TMR4_CMPLD10 = (float)F_BUS_ACTUAL / frequency / 2; // Set compare value
    TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); // Timer configuration
    TMR4_ENBL |= (1 << 0); // Enable Timer
    // Debug output to verify timer setup
    Serial.printf("Timer started with calculated period: %lu ticks\n", (uint32_t)(F_BUS_ACTUAL / (frequency * 2)));
    Serial.flush();
}
 
Best not to do prints from an ISR. That will invoke USB activity that is at a raised priority - and can cause collisions with other output elsewhere. Perhaps use a pin for Toggle on entry/exit.

If the _isr() were set at a higher priority then no other same/lower PRI interrupts would ever be invoked during its execution. Setting it at or below 32(?) should exclude most everything and then the irq disable and enable could be removed.
Code:
void dma_ch0_isr() {
    Serial.println("DMA ISR triggered");
  
    __disable_irq(); // Disable interrupts at the start of the ISR
 
Best not to do prints from an ISR. That will invoke USB activity that is at a raised priority - and can cause collisions with other output elsewhere. Perhaps use a pin for Toggle on entry/exit.

If the _isr() were set at a higher priority then no other same/lower PRI interrupts would ever be invoked during its execution. Setting it at or below 32(?) should exclude most everything and then the irq disable and enable could be removed.
Code:
void dma_ch0_isr() {
    Serial.println("DMA ISR triggered");
 
    __disable_irq(); // Disable interrupts at the start of the ISR

Thanks for the reply. So could the constant interrupting actually be the serial print acting up because it's inside an isr? I could maye toggle the built in led. Thanks for the tip about the priority. I've never used interrupts, I just copied from other peoples code, is there some kind of best practices setup procedure for interrupts, maybe do things in a certain order?
 
Last edited:
First quick look, I don't understand why this?



Normally with DMA usage you would have the ADC or ADC_ETC trigger DMA only, and then DMA generates the interrupt when transfer is complete.
Oh sorry, I was using DMA before but couldn't get it to work so I repurposed the isr function but forgot to change the name of it. It does no difference though.

Here is a new version with the ISR function name updated:

Code:
/************************************************************************************************************* */
//  ADC_ETC works but interrupts keep firing

#include "Arduino.h"
#include "ADC.h"
#include "DMAChannel.h"
DMAChannel dma;
// ADC settings
ADC *adc = new ADC();
float frequency = 1.0f;
// Buffer size and number of buffers
#define BUFFER_SIZE 10 // Size of each buffer (5 sensors per ADC)
#define NUM_BUFFERS 3  // Number of buffers to cycle through
// Define the buffers as a 2D array
DMAMEM __attribute__((aligned(32))) static uint16_t buffers[NUM_BUFFERS][BUFFER_SIZE];
// Index variable to keep track of the current buffer being filled
volatile int buffer_index = 0;
// Index of the last filled buffer
volatile int last_filled_buffer_index = 0;
// Flag to indicate new data availability
volatile bool new_data_available = false;
// Pointer to the current buffer being filled by DMA
volatile uint16_t* current_buffer = buffers[0];
extern "C" {
    extern void xbar_connect(unsigned int input, unsigned int output);
}
// Forward declarations
void adc_etc_reset();
void adc_etc_setup();
void adc_init();
void setup_timer_and_xbar(const float frequency);
void start_timer();
void adc_etc_isr();
void setup() {
    while (!Serial && millis() < 5000);
    Serial.println("Setup ADC with Software Trigger");
    // Configure ADC1 and ADC2
    adc->adc0->setAveraging(4);
    adc->adc0->setResolution(12);
    adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
    adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
    adc->adc0->singleMode();
    adc->adc0->disableInterrupts();
    adc->adc1->setAveraging(4);
    adc->adc1->setResolution(12);
    adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
    adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
    adc->adc1->singleMode();
    adc->adc1->disableInterrupts();
    adc_init();
    adc_etc_reset();  // Reset the ADC_ETC module
    adc_etc_setup();  // Set up the ADC_ETC for software triggering
    setup_timer_and_xbar(frequency);
    delay(1000);
    start_timer();
}
void loop() {
    delay(500);
    Serial.println("Values:");
    // Print the values from the last filled buffer
    for (int i = 0; i < BUFFER_SIZE; i++) {
        Serial.print(buffers[0][i]);
        if (i < BUFFER_SIZE - 1) {
            Serial.print(", ");
        }
        
    }
    Serial.println();
    // Print the values from the last filled buffer
    for (int i = 0; i < BUFFER_SIZE; i++) {
        buffers[0][i] = 0;
        buffers[1][i] = 0;
        buffers[2][i] = 0;
    }


    // Print 5 values from TRIG[0]
volatile uint32_t* trig0_results = &IMXRT_ADC_ETC.TRIG[0].RESULT_1_0; // Get the address of the first result
for (int i = 0; i < 5; i++) {
    uint16_t value = *((volatile uint16_t*)trig0_results + i); // Access the 16-bit values sequentially
    Serial.print(value);
    
    Serial.print(", ");
}
// Print 5 values from TRIG[4]
volatile uint32_t* trig4_results = &IMXRT_ADC_ETC.TRIG[4].RESULT_1_0; // Get the address of the first result
for (int i = 0; i < 5; i++) {
    uint16_t value = *((volatile uint16_t*)trig4_results + i); // Access the 16-bit values sequentially
    Serial.print(value);
    if (i < 4) {
        Serial.print(", ");
    }
}
Serial.println();
}


void adc_etc_isr() {
    Serial.println("DMA ISR triggered");
    
    // Read 5 values from TRIG[0] results
    volatile uint32_t* trig0_results = &IMXRT_ADC_ETC.TRIG[0].RESULT_1_0;
    for (int i = 0; i < 5; i++) {
        current_buffer[i] = *((volatile uint16_t*)trig0_results + i);
    }
    // Read 5 values from TRIG[4] results
    volatile uint32_t* trig4_results = &IMXRT_ADC_ETC.TRIG[4].RESULT_1_0;
    for (int i = 0; i < 5; i++) {
        current_buffer[5 + i] = *((volatile uint16_t*)trig4_results + i);
    }
    // Update the index of the last filled buffer
    last_filled_buffer_index = buffer_index;
    // Update buffer index and wrap around if necessary
    buffer_index = (buffer_index + 1) % NUM_BUFFERS;
    // Update the current buffer pointer to the next buffer
    current_buffer = buffers[buffer_index];
    // Set flag to indicate new data is available
    new_data_available = true;
    ADC_ETC_DONE0_1_IRQ |= ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);   // clear
    asm("dsb");
    
}



void adc_init() {
    ADC1_CFG |= ADC_CFG_ADTRG; // Hardware trigger
    ADC1_HC0 = 16; // ADC_ETC channel, 144 int enabled, 16 int disabled
    ADC1_GC &= ~ADC_GC_ADCO; // Continuous conversion disabled, same as adc->adcX->singleMode()
    ADC2_CFG |= ADC_CFG_ADTRG; // Hardware trigger
    ADC2_HC0 = 16; // ADC_ETC channel
    ADC2_GC &= ~ADC_GC_ADCO; // Continuous conversion disabled
}
void adc_etc_reset() {
    IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // Perform a software reset on ADC_ETC
    asm volatile("dsb"); // Ensure SOFTRST operation is completed
    IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // Clear SOFTRST
    delay(5);
}
void adc_etc_setup() {
    const uint8_t chainLength0 = 5;  // Chain length for TRIG[0]
    const uint8_t chainLength4 = 5;  // Chain length for TRIG[4]
    // Clear the TSC_BYPASS bit to ensure ADC2 is not occupied by the Touch Screen Controller
    IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_TSC_BYPASS; // Clear TSC_BYPASS (bit 30)
    // Enable TRIG[0] for xbar triggering
    IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_TRIG_ENABLE((1 << 0));
    
    // Configure TRIG[0] for ADC1
    IMXRT_ADC_ETC.TRIG[0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength0 - 1);
    // Configure chain for TRIG[0]
    IMXRT_ADC_ETC.TRIG[0].CHAIN_1_0 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(7) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(8) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(1);
    IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(12) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(11) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(1);
    IMXRT_ADC_ETC.TRIG[0].CHAIN_5_4 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_IE0(1) |
        ADC_ETC_TRIG_CHAIN_CSEL0(6) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1);
    // Configure TRIG[4] for ADC2 in sync mode with TRIG[0]
    IMXRT_ADC_ETC.TRIG[4].CTRL = 
        ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength4 - 1) |
        ADC_ETC_TRIG_CTRL_SYNC_MODE;
    // Configure chain for TRIG[4]
    IMXRT_ADC_ETC.TRIG[4].CHAIN_1_0 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(5) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(1) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(15) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(5);
    IMXRT_ADC_ETC.TRIG[4].CHAIN_3_2 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_CSEL0(0) |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_HWTS0(5) |
        ADC_ETC_TRIG_CHAIN_B2B1 |
        ADC_ETC_TRIG_CHAIN_CSEL1(13) |
        ADC_ETC_TRIG_CHAIN_IE1(0) |
        ADC_ETC_TRIG_CHAIN_HWTS1(5);
    IMXRT_ADC_ETC.TRIG[4].CHAIN_5_4 = 
        ADC_ETC_TRIG_CHAIN_B2B0 |
        ADC_ETC_TRIG_CHAIN_IE0(0) |
        ADC_ETC_TRIG_CHAIN_CSEL0(14) |
        ADC_ETC_TRIG_CHAIN_HWTS0(5);
        // Attach the interrupt vector for ADC_ETC TRIG[4] interrupt
    attachInterruptVector(IRQ_ADC_ETC0, adc_etc_isr);
    // Set interrupt priority if needed (optional, lower number = higher priority)
    NVIC_SET_PRIORITY(IRQ_ADC_ETC0, 10);  // Example priority level
    // Enable the ADC_ETC interrupt
    NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
    
}

void setup_timer_and_xbar(const float frequency) {
    CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); // Enable clock for XBAR
    // Connect TMR4 Timer output to ADC_ETC Trigger input
    xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_ADC_ETC_TRIG00); // Connect Timer to trigger TRIG[0]
}
void start_timer() {
    // Configure TMR4 Timer directly
    TMR4_ENBL &= ~(1 << 0); // Disable Timer
    TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
    TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
    TMR4_CNTR0 = 0;
    TMR4_LOAD0 = 0;
    TMR4_COMP10 = (float)F_BUS_ACTUAL / frequency / 2; // Set compare value based on desired frequency
    TMR4_CMPLD10 = (float)F_BUS_ACTUAL / frequency / 2; // Set compare value
    TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); // Timer configuration
    TMR4_ENBL |= (1 << 0); // Enable Timer
    // Debug output to verify timer setup
    Serial.printf("Timer started with calculated period: %lu ticks\n", (uint32_t)(F_BUS_ACTUAL / (frequency * 2)));
    Serial.flush();
}
 
@PaulStoffregen What I'm suspecting is that the settings for interrupts in the ADC_ETC have some kind of continuous signal for the interrupt and that the ISR is resetting it but it keeps firing or something like that. But I haven't been able to find any such settings. Or I'm just not resetting the interrupt properly. The manual is more cryptic than a book of sudoku. Also the imxrt.h has all defines but it's not that easy to understand for a hobbyist programmer.

As you can see I'm switching buffers to get triple buffering for the 10 sensors so I can access them safely later. Right now I'm just printing one buffer to keep it simple until I can get this to work. I'm also printing the contents of the result buffer for the ADC's to compare with the contents of the buffers I'm trying to set up.

I had a version of the code where I tried using eDMA to do the buffering and buffer switching but couldn't get it to work so figured I could just do this but can't even get this to work.

If I remove the Serial print from the ISR function I get this:

Setup ADC with Software Trigger
Timer started with calculated period: 75000000 ticks
Values:
2255, 2246, 2018, 2242, 2244, 2021, 2234, 2238, 2245, 2021
2256, 2246, 2021, 2242, 2245, 2021, 2233, 2237, 2245, 2021
Values:
2253, 2244, 2019, 2239, 2242, 2021, 2235, 2238, 2244, 2025
2253, 2244, 2019, 2239, 2242, 2021, 2235, 2238, 2244, 2025
Values:
2254, 2243, 2019, 2239, 2242, 2022, 2234, 2238, 2245, 2021
2254, 2243, 2019, 2239, 2242, 2022, 2234, 2238, 2245, 2021
Values:
2253, 2244, 2019, 2240, 2241, 2021, 2234, 2237, 2245, 2021
2256, 2245, 2021, 2241, 2244, 2022, 2234, 2238, 2244, 2022


The upper row is showing the contents of the result buffer from the ADC's and the lower row is showing the contents from the first buffer of my tripple buffer. It is working but if I add the Serial.print in the ISR I get the print printed a million times per second. I figured it's triggering the interrupt all the time, but maybe prints inside ISR functions are not going to work properly as Kurt pointed out. I'll try toggling an led in stead when I get home from work.
 
Last edited:
In the manual there are the DONE0_1_IRQ registers. Those are sending the interrupt I believe. Those need to be reset I think.

In the imxrt.h file there is this:

Code:
IRQ_ADC_ETC0 =          118,
IRQ_ADC_ETC1 =          119,
IRQ_ADC_ETC2 =          120,
IRQ_ADC_ETC_ERR =       121,

And then this:

Code:
#define ADC_ETC_DONE0_1_IRQ          (IMXRT_ADC_ETC.DONE0_1_IRQ)
#define ADC_ETC_DONE2_ERR_IRQ        (IMXRT_ADC_ETC.DONE2_ERR_IRQ)

I'm using the DONE0 from the last segment of trig[0] chain ( IE0(1) setting on the last segment in the ADC_ETC settings). trig[0] and Trig[4] are in sync so I figured I only need one of those. Is "IRQ_ADC_ETC0" the same thing as "ADC_ETC_DONE0_1_IRQ" ? The done0 is the first bit in that register and is cleared by writing 1 to it.

This define does that I believe:
Code:
#define ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(n)  ((uint32_t)(1<<((n) &0x7)))
I'm not sure how these things are connected.

I'm clearing it by doing this in the ISR:
Code:
ADC_ETC_DONE0_1_IRQ = ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);

Do I need to clear the IRQ_ADC_ETC0 as well somehow?

Sorry for not speaking the lingo, hopefully you can understand my ramblings.
 
Last edited:
If I write this in the ISR function:

Code:
IMXRT_ADC_ETC.TRIG[0].CTRL = ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);

Then it only interrupts once as it should but then it never interrupts again. This was a mistake but it stops it.
 
Last edited:
I would start by checking how frequently the timer is triggering.
The quad timers only have 16-bit comparison registers, you're probably going to need to chain two of them together for low frequencies.

(Optionally, connect the xbar to an IO pin instead of the timer output and trigger it manually.)

I'm clearing it by doing this in the ISR:
Code:
ADC_ETC_DONE0_1_IRQ = ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);
In your code above you have an OR ("|=") rather than direct assignment ("="). The assignment is technically better, but either of those methods should be correct.
 
Last edited:
I would start by checking how frequently the timer is triggering.
The quad timers only have 16-bit comparison registers, you're probably going to need to chain two of them together for low frequencies.

(Optionally, connect the xbar to an IO pin instead of the timer output and trigger it manually.)
I have this:

Code:
// Debug output to verify timer setup
    Serial.printf("Timer started with calculated period: %lu ticks\n", (uint32_t)(F_BUS_ACTUAL / (frequency * 2)));
    Serial.flush();

It prints: Timer started with calculated period: 75000000 ticks.

I have tested the ADC's up to 40kHz and they produce the same output at 1Hz. I have connected them to my electronic drum (it has 10 sensors) and I can see the waveforms correctly. However visually the 1Hz seems correct but I can't know for sure. The 40kHz works 100%.
 
Ok, but that doesn't show exactly what is in the timer registers, does it? You can't shove 75000000 in 16 bits, it would be truncated to 26816.
 
Ok, but that doesn't show exactly what is in the timer registers, does it? You can't shove 75000000 in 16 bits, it would be truncated to 26816.
Hmmm... I could trigger the ADC_ETC from software. However I'd have to spend several days to figure out how to do that. I have never done this stuff and just getting this far took 3 weeks.
 
Well you know how to disable the timer:
Code:
 TMR4_ENBL &= ~(1 << 0); // Disable Timer
So if you do that in the ADC_ETC IRQ handler, does it retrigger?

If it doesn't: you know the IRQ is being cleared correctly, it's just recurring constantly because the timer frequency is incorrect.
If it does: the problem is not with the timer.
 
Well you know how to disable the timer:
Code:
 TMR4_ENBL &= ~(1 << 0); // Disable Timer
So if you do that in the ADC_ETC IRQ handler, does it retrigger?

If it doesn't: you know the IRQ is being cleared correctly, it's just recurring constantly because the timer frequency is incorrect.
If it does: the problem is not with the timer.
If I disable the timer the ISR is not triggered again and neither are the ADC's (keep printing the same value). I guess it's the timer. I have to look into setting the timer properly. I had chatGPT do it for me, probably why it's not working. Thanks, I think you figured it out.
 
Last edited:
Well you know how to disable the timer:
Code:
 TMR4_ENBL &= ~(1 << 0); // Disable Timer
So if you do that in the ADC_ETC IRQ handler, does it retrigger?

If it doesn't: you know the IRQ is being cleared correctly, it's just recurring constantly because the timer frequency is incorrect.
If it does: the problem is not with the timer.
I had set the divide in the timer incorrectly. I can get down to 18Hz with the highest divider.

Here is the output now which seems more appropriate considering the frequency is set to 20Hz.

Values:
2255, 2246, 2020, 2240, 2243, 2021, 2236, 2237, 2244, 2021
2255, 2246, 2020, 2240, 2243, 2021, 2236, 2237, 2244, 2021
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered
DMA ISR triggered

You are a genius. Nobel Prizes should be renamed in your honor. I bow before your wisdom. May you continue to shine brighter than a thousand supernovae, illuminating the path for us mere mortals.

No but seriously, thank you, thousand beers to you (but not all at once haha).
 
I made two new timer setup functions with different divider settings. One that works down to 10Hz and one that is more accurate at higher frequencies that I will use later in my program as I will be using 40kHz. Seems to work. Maybe someone finds them useful.

Code:
// Timer works down to about 10Hz
void start_timer(float freq) {
    // Configure TMR4 Timer directly
    TMR4_ENBL &= ~(1 << 0); // Disable Timer
    TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
    TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
    TMR4_CNTR0 = 0;
    TMR4_LOAD0 = 0;
    // Set compare value based on desired frequency, adjusted for highest divide setting (divide by 128)
    float prescaler_value = 128.0;  // Highest divide setting
    TMR4_COMP10 = (float)F_BUS_ACTUAL / (freq * prescaler_value) / 2;
    TMR4_CMPLD10 = TMR4_COMP10; // Set compare value
    // Set the control register with the highest prescaler: PCS(15) = divide by 128
    TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(15) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); // Timer configuration
    TMR4_ENBL |= (1 << 0); // Enable Timer
    // Debug output to verify timer setup
    Serial.print("F_BUS: ");
    Serial.println(F_BUS_ACTUAL);
    Serial.printf("Timer started with calculated period: %lu ticks\n", (uint32_t)(F_BUS_ACTUAL / (freq * prescaler_value * 2)));
    Serial.flush();
}
// Timer with a lower divider, more accurate on high frequencies
void start_timer_high_freq(float freq) {
    TMR4_ENBL &= ~(1 << 0); // Disable Timer
    TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
    TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
    TMR4_CNTR0 = 0;
    TMR4_LOAD0 = 0;
    // Set prescaler value for PCS(9) = divide by 2
    float prescaler_value = 2.0;
    // Calculate compare value based on the input frequency and actual bus clock frequency
    float compare_value = (float)F_BUS_ACTUAL / (prescaler_value * freq) / 2;
    // Use rounding to get the nearest integer value
    TMR4_COMP10 = (uint32_t)round(compare_value);
    TMR4_CMPLD10 = TMR4_COMP10; // Set compare value
    // Set the control register with PCS(9): divide by 2
    TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(9) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3); // Timer configuration
    TMR4_ENBL |= (1 << 0); // Enable Timer
    // Debug output to verify timer setup
    Serial.print("F_BUS: ");
    Serial.println(F_BUS_ACTUAL);
    Serial.printf("Timer started at %.2f Hz with prescaler divide by %.0f, compare value: %lu ticks\n", freq, prescaler_value, (uint32_t)TMR4_COMP10);
    Serial.flush();
}
 
Back
Top