Serial.println inside an interrupt fails

I was trying to get acquainted with the ADC interrupts of ATmega328 (on an Arduino Uno rev3 board) when I realised that for some reason I couldn't use Serial.println inside the interrupt method. This is how my method looked like in the beginning:

ISR(ADC_vect)
{
  uint8_t lowPart = ADCL;//read first the low part!
  uint16_t  myResult = ADCH<<8 | lowPart;//the measurement
  Serial.print("x: ");
  Serial.println(myResult);
  ADCSRA |= 1<<ADSC; //Start new conversion
}

and here is my setup method just for completeness:

void setup() {
  DDRC &= ~(1<<DDC0); // Pin A0 as Input
  ADCSRA |= 1<<ADPS2 ;// Prescaler=16, i.e. 1MHz
  ADMUX |= 1<<REFS0 | 1<<REFS1; //Internal 1.1V ref used
  ADCSRA |= 1<<ADIE; // Enable interrupts over the ADC
  ADCSRA |= 1<<ADEN;// Enable the ADR

  sei();// Enable Interrupts (Global)

  ADCSRA |= 1<<ADSC;//start first conversion
  Serial.begin(9600);//Start the Serial Communicati
}

I had to move the Serial.println method inside the loop() method to get things running (while declaring myResult to be a global variable):

void loop(){
  while(1){
    Serial.print("x: ");
    Serial.println(myResult);
  }
}

why is that?

when I realised that for some reason I couldn't use Serial.println inside the interrupt method.

Did you figure out what that reason was?

Serial output is now interrupt driven. Serial.print() just stuffs the data in a buffer. Interrupts have to happen for that data to actually be shifted out. If the outgoing buffer fills up, Serial.print() blocks until there is room for the data it wants to put in the buffer to fit. That will never happen, because interrupts are disabled while your interrupt service routine is running.

PaulS:
Serial.print() just stuffs the data in a buffer. Interrupts have to happen for that data to actually be shifted out. If the outgoing buffer fills up, Serial.print() blocks until there is room for the data it wants to put in the buffer to fit. That will never happen, because interrupts are disabled while your interrupt service routine is running.

Can my interrupt wait for the other one to happen? This is a general question, since in this case it will have an effect on the performance of the ADC. Do we have any access to that interrupt that controls the flush of buffered data to the serial port?

Can my interrupt wait for the other one to happen?

Can your doorbell wait for your phone to ring before chiming? What about the poor dude standing on the porch freezing his ass off waiting for someone to call you so you can hear the doorbell ring?

No. One interrupt can't wait for another one. That is not what interrupts are about.

PaulS:
Can your doorbell wait for your phone to ring before chiming? What about the poor dude standing on the porch freezing his ass off waiting for someone to call you so you can hear the doorbell ring?

Haha! Good one! But, not true. In many programming languages (for computers), you may find entities like locks (e.g. in Java) that allow you to fully control your asynchronous operations. I understand however that interrupts are not the in-chip counterparts of threads. I tried to force flush to happen by invoking Serial.flush(), but without success! Is there a way to write to serial in some different way that is not controlled by interrupts?

But, not true.

Well, it is on the Arduino. And, I think that you'll remember that that is what THIS forum is about.

Is there a way to write to serial in some different way that is not controlled by interrupts?

Look at the HardwareSerial class for 0023 or earlier, and implement your own version for 1.0+.

Don't expect to be able to switch back and forth between interrupt driven and non-interrupt driven serial output on the same pins (or, more likely, on the same Arduino), though.

Before you go too far down that rabbit hole, though, you might want to repeat the ISR mantra a few times. It's really simple. "Fast! Fast! Fast!".

why is that?

Re-entrant calls: rs232 is very slow so your isr can get triggered in the middle of a rs232 transmission. When your isr calls the same transmission routines, it creates an re-entrancy.

Solution: 1) don't call any functions or execute time-consuming tasks in an isr. isr is supposed to be quick; 2) make two versions of the transmission routines and dedicate one version to the isr.

Search the forum for interrupt and serial.print. The distilled wisdom you'll find is - "just say no". Serial comms are slow - putting anything slow in an interrupt routine is asking for trouble.

It seems to me two bits are missing:

  1. the global variable needs to be declared volatile
  2. thee should be a way for the adc isr to inform the main loop() that a new reading has completed, like a bool flag (this should also be volatile).

Am I correct ?

tuxduino:

  1. the global variable needs to be declared volatile

Yes, I noticed that later and I changed it, but it doesn't affect the behaviour of the program. Serial.println inside the interrupt method, just doesn't work.

tuxduino:
2) thee should be a way for the adc isr to inform the main loop() that a new reading has completed, like a bool flag (this should also be volatile).

This is not necessary. This is the meaning of a volatile variable. Unless, I don't get your point...

He was saying rather than call serial functions from inside of the isr, set a flag in the isr and process that flag (calling the serial functions, etc.) in the main loop() - the typical way to use isr.

chung:

tuxduino:

  1. the global variable needs to be declared volatile

Yes, I noticed that later and I changed it, but it doesn't affect the behaviour of the program. Serial.println inside the interrupt method, just doesn't work.

Of course it doesn't. I was thinking about the version where you repeatedly call Serial.println() inside loop().

chung:

tuxduino:
2) thee should be a way for the adc isr to inform the main loop() that a new reading has completed, like a bool flag (this should also be volatile).

This is not necessary. This is the meaning of a volatile variable. Unless, I don't get your point...

No, volatile just tells the compiler that the variable could change its value at any time, thus it must avoid certain types of code optimizations, like caching the variable value inside a register...