MartyBrown
Member
Hello together!
I have a question regarding the maximum ADC Speed of the teensy 4.0. According to the datasheet of the IMXRT1060 the minimal total conversion time is ~750nS,
but with the code below I'm way too fast for that and getting conversion times of about ~200nS!!! (For 12Bit!!!)
The highest possible clock for the ADCs should be 40Mhz, so the values I'm measuring can't be accurate!
Does anyone know where these strange measurements come from?
Have I set any of the registers wrong, or is the measurement itself done wrong?
Thank you in advance for any help
Measurements:
(for getting Nanoseconds: 10^9/ticks)
Code:
I have a question regarding the maximum ADC Speed of the teensy 4.0. According to the datasheet of the IMXRT1060 the minimal total conversion time is ~750nS,
but with the code below I'm way too fast for that and getting conversion times of about ~200nS!!! (For 12Bit!!!)
The highest possible clock for the ADCs should be 40Mhz, so the values I'm measuring can't be accurate!
Does anyone know where these strange measurements come from?
Have I set any of the registers wrong, or is the measurement itself done wrong?
Thank you in advance for any help
Measurements:
(for getting Nanoseconds: 10^9/ticks)
Code:
Code:
//Verwendetes Datenblatt: IMXRT1060 Reference Manual
#include <DMAChannel.h>
#define PrintRegister(x) Serial.print(#x" 0x"); Serial.println(x, BIN); //bequeme Schreib-Definition für seriellen Monitor (zur Ausgabe von Registerwerten)
#define BufferSize 1 //Größe aller DMA-Buffer
/* ADC Muxing Options (für ADC1-Inputs (auch mit ADC2 messbar!) S. 289):
ADC1_IN7 GPIO_AD_B1_02 (A0) // ADC1_IN8 GPIO_AD_B1_03 (A1) // ADC1_IN12 GPIO_AD_B1_07 (A2)
ADC1_IN11 GPIO_AD_B1_06 (A3) // ADC1_IN6 GPIO_AD_B1_01 (A4) // ADC1_IN5 GPIO_AD_B1_00 (A5)
ADC1_IN15 GPIO_AD_B1_10 (A6) // ADC1_IN0 GPIO_AD_B1_11 (A7) // ADC1_IN13 GPIO_AD_B1_08 (A8)
ADC1_IN14 GPIO_AD_B1_09 (A9) // ADC1_IN1 GPIO_AD_B0_12 (A10) // ADC1_IN2 GPIO_AD_B0_13 (A11) */
#define pinMessungA 7 //A0 --> langsamer => Strom!
#define pinMessungB 14 //A9 --> schneller => Spannung! (wird als 2. initialisiert => Priorität pinMessungB > Priorität pinMessungA
//ADC General Status-Register S. 3494
bool ADACT1; bool ADACT2; //Conversion Active Flag
bool CALF1; bool CALF2; //Calibration Failed Flag
bool AWKST1; bool AWKST2; //Asynchronous wakeup interrupt status (0 = no asynchronous interrupt)
DMAMEM static uint16_t SpannungBufferDMA[BufferSize]; //Buffer für Spannung im DMA-Speicher
DMAChannel dma1(false);
DMAMEM static uint16_t StromBufferDMA[BufferSize]; //Buffer für Strom im DMA-Speicher
DMAChannel dma2(false);
volatile uint32_t ticks = 0;
volatile uint32_t ticks2 = 0;
void setup() {
Serial.begin(115200); //Baudrate einstellen für serielle Verbindung
while (!Serial);
setupADCs(pinMessungA, pinMessungB);
if (checkADCs()) { //Laufen beide ADCs fehlerfrei?
Serial.println("\nBeide ADCs erfolgreich initialisiert!");
} else {
Serial.println("\nBeide ADCs laufen nicht! (INIT FEHLER)\n");
}
delay(1000);
}
void loop() {
printReadings(); //Speed in nS = (10^9/ticks)
arm_dcache_delete(SpannungBufferDMA, BufferSize + 1); //Cache leeren + 1?
arm_dcache_delete(StromBufferDMA, BufferSize + 1); //Cache leeren
delay(1000);
}
bool checkADCs() {
/* AWKST | CALF | ADACT
ADCx_GS = 3Bit... 1 0 1
Maske mit & für ADACT: 0 0 1 */
ADACT1 = ADC1_GS & 001; ADACT2 = ADC2_GS & 001;
CALF1 = ADC1_GS & 010; CALF2 = ADC2_GS & 010;
AWKST1 = ADC1_GS & 100; AWKST2 = ADC2_GS & 100;
if (ADACT1 && ADACT2 && !CALF1 && !CALF2 && !AWKST1 && !AWKST2) return 1;
return 0;
}
void setupADCs(int pin1, int pin2) {
//Datasheet Seite 3449 (i.MX RT1064)
cli(); //Interrupts deaktivieren
Serial.println("Setup ADCs...\n");
//ADC-1:
ADC1_GC = 0x42; //1000010
ADC1_CFG = 0x408; //10000001000
ADC1_HC0 = pin1; //Kanal 7 = A0
PrintRegister(ADC1_GC);
PrintRegister(ADC1_CFG);
PrintRegister(ADC1_HC0);
//DMA für ADC1 initialisieren:
dma1.begin(true); //Speicher für DMA-Kanal allozieren
dma1.source((uint16_t &) ADC1_R0); //ADC1 als Triggerquelle wählen
dma1.destinationBuffer(SpannungBufferDMA, BufferSize + 1); //Ziel wählen
dma1.interruptAtCompletion();
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1); //DMA durch ADC1 triggern lassen!
//ADC-2:
ADC2_GC = 0x42; //11000010
ADC2_CFG = 0x408; //10000001000
ADC2_HC0 = pin2; //Kanal 14 = A9
PrintRegister(ADC2_GC);
PrintRegister(ADC2_CFG);
PrintRegister(ADC2_HC0);
//DMA für ADC2 initialisieren:
dma2.begin(true); //Speicher für DMA-Kanal allozieren
dma2.source((uint16_t &) ADC2_R0); //ADC2 als Triggerquelle wählen
dma2.destinationBuffer(StromBufferDMA, BufferSize + 1);
dma2.interruptAtCompletion();
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC2);
//Direct Memory Access Interrupts aktivieren:
dma1.attachInterrupt(dma1_isr);
dma1.enable();
dma2.attachInterrupt(dma2_isr);
dma2.enable();
sei(); //Interrupts wieder aktivieren
}
void dma1_isr(void) { //ADC
dma1.clearInterrupt();
ticks++; //Sample-Counter
asm volatile ("dsb"); //Inline Assembler Anweisung
/* https://developer.arm.com/docs/dui0646/a/the-cortex-m7-instruction-set/instruction-set-summary
DSB acts as a special data synchronization memory barrier.
Instructions that come after the DSB, in program order, do not execute until the DSB instruction completes.
The DSB instruction completes when all explicit memory accesses before it complete. */
}
void dma2_isr(void) { //ADC
dma2.clearInterrupt();
ticks2++; //Sample-Counter
asm volatile ("dsb");
}
void printReadings() { //Debugging
static int prev;
static int prev2;
Serial.printf("%d ticks A0 = %d \n", ticks - prev, SpannungBufferDMA[0]);
prev = ticks;
Serial.printf("%d ticks A9 = %d \n", ticks2 - prev2, StromBufferDMA[0]);
prev2 = ticks2;
}
Last edited by a moderator: