ADC blocking print?

Hi, I'm struggling with a curious effect.
With modified examples of this forum I'm testing ADC of Due with register programming for a planned project.

  • With one SW trigger I'd like to see ADC results from one or more enabled channels depending on the variable chan (in setup()).
  • The converted data shall be stored to arrays via interrupt.
  • Every interrupt shall store ADC_ISR, time in us and ADC_LCDR
  • interrupts shall be fired by EOC of enabled channel(s) and by DRDY
  • converted data shall be printed once within loop()
#define PRINT(s, v) \
  { \
    Serial.print(F(s)); \
    Serial.print(v); \
  }

#define PRINTHEX(s, v) \
  { \
    Serial.print(F(s)); \
    Serial.print(v, HEX); \
  }

#define TICPIN (7)

volatile uint8_t dcnt = 0;
volatile uint16_t dat[16];
volatile uint32_t disr[16], etim[16];
uint32_t stim;
volatile boolean tic=false;

void ADC_Handler() {
  if (dcnt < 16) {
    disr[dcnt] = ADC->ADC_ISR;
    etim[dcnt] = micros();
    dat[dcnt++] = (uint16_t)(ADC->ADC_LCDR);
  }

  tic != tic;
  if (tic) digitalWrite(TICPIN, HIGH);
  else digitalWrite(TICPIN, LOW);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  Serial.println();
  PRINT("Start ", __FILE__);
  Serial.println();

  pinMode(TICPIN, OUTPUT);

  uint32_t chan = 0x81;
  NVIC_EnableIRQ(ADC_IRQn);
  ADC->ADC_CHDR = 0xFFFF ; // disable all channels
  ADC->ADC_CHER = chan ;  // enable channels according to cher
  ADC->ADC_CGR = 0x15555555 ; // All gains set to x1
  ADC->ADC_COR = 0x00000000 ; // All offsets off
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0); // no HW trigger

  ADC->ADC_IDR = 0xFFFFFFFF;
  ADC->ADC_IER = chan | (1<<24);  // EOC of enabled channels and DRDY

  ADC->ADC_EMR = ADC->ADC_EMR | (1 << 24); // set TAG: Channel no. in converted data
  ADC->ADC_CR = 2;    // start

  PRINTHEX("  CHSR=0x", ADC->ADC_CHSR);
  PRINTHEX("  MR=0x", ADC->ADC_MR);
  PRINTHEX("  IMR=0x", ADC->ADC_IMR);
  PRINT("    LCDR=", ADC->ADC_LCDR);
  Serial.println();

  Serial.println(F("  Zeit\tWert\tISR "));
  Serial.println(stim);
  delay(5);
}

void loop() {
  // put your main code here, to run repeatedly:
  if (dcnt) {
    for (int i = 0; i < dcnt; i++) {
      Serial.print(etim[i]); Serial.print("\t");
      Serial.print(dat[i]); Serial.print("\t");
      Serial.println(disr[i]);
    }
    dcnt=0;
  }
  
}

Within setup() 4 register contents shall be printed. But Serial Monitor only shows

Start H:\Arduino\examples\sketch_jul16a\sketch_jul16a.ino
CHSR=0x81 MR=0x103

It seems that register ADC_MR is printed only partially and registers ADC_IMR and ADC_LCDR not at all. All following prints are blocked. No converted data are printed, but on an oscilloscope I can see a tic pulse from an interrupt.

Any tips are welcome to solve this mystery.

this would need to be in a critical section as the variables can change during execution since your interrupts are running

you could disable interrupts, check if dcnt is not null and if so make a copy of the data, reset the counter and set a flag that you have something to print and then enable interrupts again. Then if the flag was set, print the data from the copy.

remember print() will block if the outgoing buffer is full. How often do you capture data?

I don't believe that the print statements within loop() are critical because there is only a single SW trigger command within setup(), and according to the Due datasheet the ADC will convert all enabled channels after a trigger and then wait for another trigger.
Further, after SW trigger there are several print statements and a delay of 5 ms. Therefore all conversion results (2 in my example) should be stored to the arrays before loop() begins.

Really the problem of blocked prints already occurs within setup().
Only the first 2 print statements out of 7 are shown in the Serial Monitor.

Anyway the problem is caused by interrupts. If the line

NVIC_EnableIRQ(ADC_IRQn);

is commented out then all print statements within setup() are completed.

But what is wrong with the interrupt handler?

I don’t own a due - so I’m not familiar with the details of its architecture

I also haven’t worked with Due, but just by analogy - shouldn’t you reset the interrupt flag in your handler?

I found part of the solution:
In my sketch ADC is configured to fire interrupts when EOC flags are set and when DRDY flag is set.

ADC->ADC_IER = chan | (1<<24);  // EOC of enabled channels and DRDY

Because DRDY flag is set with each of the EOC flags there are always two interrupt requests with each of the flags. This might be an internal conflict. Whatever, because the interrupt handler always reads register ADC_LCDR, using only DRDY flag works.

This is a solution but there is a further curious effect which I will describe in a new post.

For my previous post "ADC blocking print?" I found a solution.

After a lot of testing I am fighting with a new effect described in this new post.
The sketch is slightly modified.

#define PRINT(s, v) \
  { \
    Serial.print(F(s)); \
    Serial.print(v); \
  }

#define PRINTHEX(s, v) \
  { \
    Serial.print(F(s)); \
    Serial.print(v, HEX); \
  }

#define TICPIN (7)    // D7, PC23

volatile uint8_t dcnt = 0;
volatile uint16_t dat[16];
volatile uint32_t disr[16], etim[16];
uint32_t stim;

uint8_t nrchan = 0;

uint8_t opt_print = 0; /* 0: compute and print nrchan early
                          1: compute and print nrchan late */

void ADC_Handler() {
  REG_PIOC_SODR = 0x00800000;  // TICPIN auf 1
  if (dcnt < 16) {
    disr[dcnt] = ADC->ADC_ISR;
//    etim[dcnt++] = micros();
    dat[dcnt++] = (uint16_t)(ADC->ADC_LCDR);
  }

  
  REG_PIOC_CODR = 0x00800000;  // TICPIN auf 0
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  Serial.println();
  PRINT("Start ", __FILE__);
  Serial.println();

//  pinMode(TICPIN, OUTPUT);
//  digitalWrite(TICPIN, LOW);
  REG_PIOC_PER = 0x00800000;   // assign TICPIN (PC23) to peripheral IO controller PIOC
  REG_PIOC_OER = 0x00800000;   // set TICPIN for ouput
  REG_PIOC_CODR = 0x00800000;  // clear TICPIN

  PRINT("opt_print=", opt_print); // print option
  Serial.println();

  uint32_t chan = 0x2085;   // enabled channels

  if (opt_print == 0) {   // compute and print nrchan early
    PRINTHEX("chan=0x", chan);
    for (int i = 0; i < 16; i++) {
      if (chan & 1) {
        nrchan++;
        PRINT(" ch",i);
      }
      chan = chan >> 1;
    }
    PRINT("  no. channels: ", nrchan);
    Serial.println();

    Serial.println(F("  Time\tchan/Value\tISR "));
  }

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;      // ADC power on

  ADC->ADC_CHDR = 0xFFFF ; // disable all channels
  ADC->ADC_CHER = chan ;  // enable channels according
  ADC->ADC_CGR = 0x15555555 ; // All gains set to x1
  ADC->ADC_COR = 0x00000000 ; // All offsets off
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | ADC_MR_LOWRES; // no HW trigger, 10bit /ADC_MR_PRESCAL(value)

  ADC->ADC_IDR = 0xFFFFFFFF;
  ADC->ADC_IER = ADC_IER_DRDY;  // only DRDY
  ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);     // For frequencies > 500 KHz, (datasheet Table 45.29)

  ADC->ADC_EMR = ADC->ADC_EMR | (1 << 24); // set TAG: Channel no. in converted data
  NVIC_EnableIRQ(ADC_IRQn);
  ADC->ADC_CR = 2;              // start conversions
  stim = micros();
  REG_PIOC_SODR = 0x00800000;  // set TICPIN
  REG_PIOC_CODR = 0x00800000;  // clear TICPIN

  if (opt_print == 1) {   // compute and print nrchan late
    PRINTHEX("chan=0x", chan);
    for (int i = 0; i < 16; i++) {
      if (chan & 1) {
        nrchan++;
        PRINT(" ch",i);
      }
      chan = chan >> 1;
    }
    PRINT("  no. channels: ", nrchan);
    Serial.println();

    Serial.println(F("  Time\tchan/Value\tISR "));
  }
  delay(300);  // wait until conversions have completed

  if (dcnt) {
    Serial.println(stim);
    for (int i = 0; i < dcnt; i++) {
      Serial.print(etim[i]); Serial.print("\t");
      Serial.print((dat[i] >> 12) & 0xF);
      PRINT("/", dat[i] & 0xFFF);
      PRINTHEX("\t\t0x", disr[i]);
      Serial.println();
    }
    dcnt = 0;
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

The sketch contains a block of code to compute the number of enabled channels (variable chan) and do some printing.
There is a new variable, opt_print, which defines if this block of code will be executed before (opt_print=0) or after (opt_print=1) configuration of ADC.

With opt_print=1, i.e. block of code after configuration of ADC everthing is ok. Output of Serial Monitor:

Start H:\Arduino\examples\sketch_jul16a\sketch_jul16a.ino
opt_print=1
chan=0x2085 ch0 ch2 ch7 ch13 no. channels: 4
Time chan/Value ISR
1126
0 0/943 0x19000001
0 2/168 0x19000005
0 7/206 0x19000085
0 13/0 0x19002085

But with opt_print=0 there are no converted data from ADC, i.e. the interrupt handler never is fired. Serial Monitor:

Start H:\Arduino\examples\sketch_jul16a\sketch_jul16a.ino
opt_print=0
chan=0x2085 ch0 ch2 ch7 ch13 no. channels: 4
Time chan/Value ISR

Finally I want to use the ADC in a larger project. This sketch is only for testing. But before knowing the real background for this very curious behaviour I don't dare to include it into my project.

Therefore all tips/explanations are very, very welcome.

My experiences up to now with testing ADC are accumulated in a document attached to this post.
ExperiencesWithDueADC.pdf (799.7 KB)

I have merged your topics due to them having too much overlap on the same subject matter @SupArdu.

In the future, please only create one topic for each distinct subject matter and be careful not to cause them to converge into parallel discussions.

The reason is that generating multiple threads on the same subject matter can waste the time of the people trying to help. Someone might spend a lot of time investigating and writing a detailed answer on one topic, without knowing that someone else already did the same in the other topic.

Thanks in advance for your cooperation.