ADC_ETC Not Triggering (Software & Hardware Mode)

teensy

Member
Hello everyone,

I'm facing a persistent issue with the ADC_ETC module on an i.MX RT1060 (Teensy 4.1) where it fails to initiate a trigger, both in software trigger mode and when triggered externally via XBAR from a PIT. I've been debugging this extensively and would appreciate any insights or working examples.

Goal:
Implement a hardware-triggered ADC conversion sequence: PIT → XBAR → ADC_ETC → ADC1 → DMA. Initially, I attempted to verify ADC_ETC functionality using its software trigger mechanism.
Problem Description:
1. ADC_ETC Software Trigger Fails:
When attempting to use the ADC_ETC's software trigger (setting `TRIGx_CTRL.TRIG_MODE = 1` and then `TRIGx_CTRL.SW_TRIG = 1`), the `SW_TRIG` bit (Bit 0) is correctly set by the CPU.
However, the `SW_TRIG` bit is not cleared by the ADC_ETC hardware as expected. The i.MX RT1060 Reference Manual (Rev. 3, 07/2021, p. 3469 for `TRIGx_CTRL.SW_TRIG`) states: "NOTE: This field is self-clearing." (or in another section, "clear by hardware after the trigger an event to ADC module").
Consequently, no ADC_ETC "DONE0" interrupt is generated, and no ADC conversion appears to be initiated by the ADC_ETC. This behavior was observed for both TRIG0 and TRIG1.

2. ADC_ETC Hardware Trigger Fails:
When attempting the full hardware chain (PIT → XBAR → ADC_ETC → ADC1), the ADC_ETC also does not generate any "DONE0" interrupts.
The PIT is confirmed to be generating interrupts (and thus trigger pulses).
XBAR (XBARA1_SEL51) is configured to route the PIT output (e.g., `kXBARA1_InputPitTrigger0` which is input 56) to the ADC_ETC trigger input (e.g., `ADC_ETC_TRIG00` which is XBARA output 103). The register value `0x3800` confirms this routing.
The ADC_ETC (e.g., TRIG0) is configured for hardware trigger mode (`TRIGx_CTRL.TRIG_MODE = 0`).

Key Debugging Steps & Findings:
Systematic Minimal Example Tests: We've used a minimal test environment (`setup()` in Arduino/Teensy) to isolate the ADC_ETC.

ADC1 Configuration:
ADC1 is calibrated successfully.- `ADC1_CFG` is set for ADC_ETC control (`ADTRG=0`). We also tested with `ADTRG=1` (as suggested by an NXP SDK example for RT1170 ADC_ETC software trigger), but this didn't resolve the ADC_ETC software trigger issue on the RT1060. For the hardware chain, `ADTRG=0` is used.
`ADC1_GC` is set to `0x00000001` (ensuring `DMAEN=1`, `ADCO=0`, `CAL=0`) after identifying an issue with a local `imxrt.h` macro `ADC_GC_DMAEN` being defined as `(1U << 1)` (setting `ADCO`) instead of the expected `(1U << 0)` for `DMAEN`.
`ADC1_HC0` is configured for the target ADC channel (e.g., channel 1).
`ADC1_GS` confirms `ADACT=0` (ADC idle) before trigger attempts.

ADC_ETC Configuration:
Clocks for ADC_ETC and ADC1 are enabled in CCM.- ADC_ETC soft-reset is performed (`ADC_ETC_CTRL.SOFTRST` cleared).
Observed: After clearing `SOFTRST`, `ADC_ETC_CTRL` reads `0x40000000` (`TSC_BYPASS=1`), which is unexpected as the reset value for `TSC_BYPASS` is `0`.
However, `ADC_ETC_CTRL` is subsequently configured manually.
`ADC_ETC_CTRL`: `TRIG_ENABLE` set for the active trigger (e.g., `TRIG0_ENABLE=1`), `PRE_DIVIDER` tested with `0` and `1`. Other bits like `DMA_MODE_SEL`, `TSC_BYPASS` (after manual config) are `0`.
'ADC_ETC_TRIGx_CTRL`: `TRIG_MODE` set to `1` for software tests, `0` for hardware tests. `TRIG_CHAIN=0` (1 segment), `SYNC_MODE=0`, `TRIG_PRIORITY=0`.
`ADC_ETC_TRIGx_CHAIN_1_0`: Configured for `IE0=1` (Done0 interrupt), `HWTS0` pointing to an ADC hardware control register (e.g., `1U<<0` for `ADC_HC0`), `CSEL0` for the ADC channel, `B2B0=0`. (e.g., value `0x2011`).
`ADC_ETC_TRIGx_COUNTER`: `INIT_DELAY` and `SAMPLE_INTERVAL` tested with various values (0, 10, 255).
'ADC_ETC_DMA_CTRL`: `TRIGx_ENABLE` for DMA request is set for the active trigger (e.g., `(1U << 0)` for TRIG0).- ADC_ETC interrupt (IRQ_ADC_ETC0) is enabled in NVIC and an ISR is attached.

Error Checks: `ADC_ETC_DONE2_ERR_IRQ` register shows no error flags.

Errata ERR052412 (SW_TRIG stuck high): Aware of this, but the issue is that `SW_TRIG` isn't cleared on the *first* attempt, not just subsequent ones.

DMAMUX/DMA: Configured for `ADC1_COCO` (source 40) to a DMA channel. TCD is set up. (This part is more relevant once ADC_ETC triggers).

Current Status & Presumed Cause:
Despite configurations appearing correct for both software and hardware triggers, and ADC1 being perfectly prepared, the ADC_ETC module does not seem to start its trigger process. The primary indicator for software triggers is the `SW_TRIG` bit not being cleared by hardware. For hardware triggers, it's the absence of ADC_ETC interrupts.

This leads us to suspect a more fundamental issue:

1. ADC_ETC Internal Clocking Problem: While register access (via `ipg_clk`) works, the internal core logic of ADC_ETC responsible for processing triggers might not be clocked correctly or at all.
2. Undocumented Initialization Requirement or System-Level Lock: There might be a critical, uninitialized bit elsewhere or a system state preventing ADC_ETC operation.

Has anyone successfully used the ADC_ETC (especially software trigger or a simple XBAR-triggered chain) on an i.MX RT1060, particularly with Teensy 4.1 / Arduino environment, at the register level?Are there known critical ADC_ETC initialization steps or register settings beyond what's commonly documented that are essential for its basic operation?Could there be specific interactions or default configurations in the Teensy 4.1 core that might interfere with direct ADC_ETC register manipulation?Any working, minimal, register-level examples for ADC_ETC software or hardware triggering on RT1060 would be immensely helpful.

Any pointers or suggestions would be greatly appreciated, as we've exhausted most standard debugging approaches for register configuration.

Thanks!
Chris
 
And here is a minimal sketch:

#include <Arduino.h>

volatile uint32_t minimal_adc_etc_isr_count_v7 = 0; // Neuer Zähler für Klarheit

void adc_etc_minimal_isr_v7() { // Neuer ISR-Name
if (ADC_ETC_DONE0_1_IRQ & ADC_ETC_DONE0_1_IRQ_TRIG_DONE1(0)) { // Prüfe TRIG1 DONE0
ADC_ETC_DONE0_1_IRQ |= ADC_ETC_DONE0_1_IRQ_TRIG_DONE1(0); // Lösche Flag für TRIG1, DONE0 (Bit 1)
minimal_adc_etc_isr_count_v7++;
}
// Optional auch TRIG0 prüfen, falls beide aktiv wären
// if (ADC_ETC_DONE0_1_IRQ & ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0)) {
// ADC_ETC_DONE0_1_IRQ |= ADC_ETC_DONE0_1_IRQ_TRIG_DONE0(0);
// }
}

void setup() {
Serial.begin(115200);
unsigned long startTime = millis();
while (!Serial && (millis() - startTime < 4000)) { yield(); }
Serial.println("\n=== ADC_ETC Minimal SW Trigger Test (v7 - Fokus TRIG1) ===");

// 1. Clocks (wie gehabt)
CCM_CCGR1 |= CCM_CCGR1_ADC1(CCM_CCGR_ON);
CCM_CCGR1 |= (3 << 0);
delay(1);
Serial.println("Clocks aktiviert.");

// 2. ADC1 Konfiguration (wie in v6, wo GC=0x01 erreicht wurde)
// ADC1_CFG = 0; // Bisheriger Ansatz
ADC1_CFG = ADC_CFG_ADTRG; // Setze ADTRG (Bit 13) auf 1, Rest Standard (0)
// ADC_CFG_ADTRG ist (1U << 13)

ADC1_HC0 = ADC_HC_ADCH(1);
Serial.println("Starte ADC1 Kalibrierung...");
ADC1_GC = ADC_GC_CAL;
startTime = millis();
while (ADC1_GC & ADC_GC_CAL) {
if (millis() - startTime > 100) { Serial.println("ADC1 Kalibrierung Timeout!"); break; }
yield();
}
if (ADC1_GS & ADC_GS_CALF) { Serial.println("ADC1 Kalibrierung fehlgeschlagen.");}
else if (!(ADC1_GC & ADC_GC_CAL)) { Serial.println("ADC1 Kalibrierung erfolgreich.");}
else { Serial.println("ADC1 Kalibrierung unklar.");}
ADC1_GC = 0x00000001; // Direkt setzen für DMAEN=1, ADCO=0
Serial.printf("ADC1_CFG: 0x%08X, ADC1_GC: 0x%08X, ADC1_GS: 0x%08X\n", ADC1_CFG, ADC1_GC, ADC1_GS);

// 3. ADC_ETC Konfiguration für TRIG1
if (ADC_ETC_CTRL & ADC_ETC_CTRL_SOFTRST) {
ADC_ETC_CTRL &= ~ADC_ETC_CTRL_SOFTRST;
delayMicroseconds(50);
}
// Aktiviere nur TRIG1, PRE_DIVIDER = 0
ADC_ETC_CTRL = ADC_ETC_CTRL_TRIG_ENABLE(1U << 1) | ADC_ETC_CTRL_PRE_DIVIDER(0);

ADC_ETC_TRIG1_CTRL = 0; // Für TRIG1
// HWTS0 für TRIG1 zeigt auf ADC_TRIG1 (Hardware Trigger Source 1)
// CSEL0 für TRIG1 kann weiterhin ADC Channel 1 sein
ADC_ETC_TRIG1_CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_IE0(0b01) |
ADC_ETC_TRIG_CHAIN_HWTS0(1U << 1) | // HWTS0 -> ADC_TRIG1
ADC_ETC_TRIG_CHAIN_CSEL0(1U << 0);
ADC_ETC_TRIG1_COUNTER = ADC_ETC_TRIG_COUNTER_INIT_DELAY(0); // Kürzester Delay

// Interrupt Setup (IRQ_ADC_ETC0 ist für DONE0/DONE1 aller Trigger)
attachInterruptVector(IRQ_ADC_ETC0, adc_etc_minimal_isr_v7);
NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
NVIC_SET_PRIORITY(IRQ_ADC_ETC0, 0);
Serial.println("ADC_ETC für TRIG1 konfiguriert.");
Serial.printf("ADC_ETC_CTRL: 0x%08X\n", ADC_ETC_CTRL);
Serial.printf("ADC_ETC_TRIG1_CTRL: 0x%08X\n", ADC_ETC_TRIG1_CTRL);
Serial.printf("ADC_ETC_TRIG1_CHAIN_1_0: 0x%08X\n", ADC_ETC_TRIG1_CHAIN_1_0);

uint32_t adc1_gs_val_before_sw = ADC1_GS;
Serial.printf("ADC1_GS vor SW Trig: 0x%08X (ADACT sollte 0 sein)\n", adc1_gs_val_before_sw);

// 4. Software-Trigger Versuch für TRIG1
Serial.println("\nVersuche Software-Trigger für TRIG1...");
ADC_ETC_TRIG1_CTRL |= ADC_ETC_TRIG_CTRL_TRIG_MODE;
delayMicroseconds(5);
if (!(ADC_ETC_TRIG1_CTRL & ADC_ETC_TRIG_CTRL_SW_TRIG)) {
ADC_ETC_TRIG1_CTRL |= ADC_ETC_TRIG_CTRL_SW_TRIG;
} else {
Serial.println(" WARNUNG: SW_TRIG für TRIG1 war bereits 1!");
}
Serial.printf("ADC_ETC_TRIG1_CTRL nach Setzen SW_TRIG: 0x%08X\n", ADC_ETC_TRIG1_CTRL);
delay(50);

Serial.printf("ADC_ETC_TRIG1_CTRL nach Wartezeit: 0x%08X (SW_TRIG sollte 0 sein)\n", ADC_ETC_TRIG1_CTRL);
Serial.printf("Minimal ADC_ETC ISR Count (v7): %lu\n", minimal_adc_etc_isr_count_v7);
Serial.printf("ADC_ETC_DONE0_1_IRQ (Bit 1 für TRIG1_DONE0): 0x%08X\n", ADC_ETC_DONE0_1_IRQ);

ADC_ETC_TRIG1_CTRL &= ~ADC_ETC_TRIG_CTRL_TRIG_MODE;
Serial.println("\n=== Minimaltest (TRIG1) beendet ===");
}

void loop() {
delay(1000);
}
 
Back
Top