attachInterrupt and serial io

I've come up against a situation where I may want to use an external interrupt and also be able to receive info through serial io. I see that if I am in the attachInterrupt function all serial io received will be lost. Is there any way around this? I'm happy to not use attachInterrupt and write the interrupt code without it, but I'd like to understand what the issue is.

Thanks!

The only lost serial input is that which arrives while your interrupt handler is running. This is a result of the fact that the serial input is interrupt driven, and the way we have the chip configured, only one interrupt handler can run at a time. It would be the same whether or not you used attachInterrupt() to register your interrupt handler.

Typically, you want to do something very short in your interrupt handler (like set a flag or increment a counter) and do most of the processing in your main loop().

Thanks!

So I can't really fix this in software? I know to keep my interrupt routine very brief, and I'm not sure how much serial io I will be receiving, so this might not be a problem. I just need to understand the options.

There is, I believe, an option to allow nested interrupts, but that could cause other problems.

You might just give it a shot and see what happens.

I have worked with nested interrupts in the past and feel confident that I can deal with the possible problems associated with them, so I may try that. Can you point me to any information on them?

Thanks again for the help!

Check out page 15 of the ATmega168 datasheet. It sounds like you simply write a 1 to the interrupt enable bit at the start of your handler.

You shouldn’t lose data (using the interrupt-driven serial IO routines on the hardware USART) just because it arrives during your other interrupt service routine. The uart interrupt will remain queued until your ISR is done, and then the UART ISR will run. As long as your other ISR does not last so long that the hardware UART receives TWO more characters and overflows the internal two-byte buffer, you should be fine.

Thank you! All I am doing in the interrupt is setting a flag to true, so I should be fine :)

I'm currently running into the same problem with interrupts and serial. I'm use the hardware interrupt to to count inputs from an encoder on an engine(so all the ISR is doing is incrementing) , while constantly sending data to the computer.

However, at higher speeds (around 3500 interrupts per second and upwards) the data is corrupted when it arrives at the PC. I basically start receiving a constant stream of garbage.

Is there anyway around this? like higher baud maybe or changing interrupt priorities. or is this a lost cause?

Given the fact that the UART is a piece of hardware driven by the processor's clock, the only way corrupt serial output could be caused by a high interrupt frequency is if there is a serious bug in the hardware. That most certainly is not the case.

What is very likely the case is that you have a bug in your Sketch. If you would like help, you will have to post your Sketch. Please use "code tags".

sure, if you don’t mind having a look. I had to leave out some sections due to the post length limit but I think all the important stuff is there.

As far as I can tell the receiving part of the code works, and I haven’t any problems with it.

in summary:
-using 9600 baud rate.

-sending data to computer every 200ms, consisting of:
chr \n -identifier for data
bh bl \n - speed (high byte and low byte of long)
bh bl \n - time
bh bl \n - torque

-interrupts can occur up to 4000 times per second. at the moment everything works perfectly up to about 3200/sec.

someone suggested to me that increasing the baud rate might reduce the likelihood of of the Serial being interrupted, but if you say this can’t happen then it probably won’t solve anything. I haven’t had the chance to test it.

Thanks for the help

#include <avr/io.h>
#include <avr/interrupt.h>
#include <Servo.h>
#include <stdlib.h>

void setup(){

  pinMode(ENCODER_PIN, INPUT);

  Serial.begin(9600);   
  attachInterrupt(0, encoder, HIGH);
  throttle_servo.attach(SERVO_PIN, SERVO_PULSE_MIN, SERVO_PULSE_MAX);  
  throttle_servo.write(0);
}


//*************************************************************
// MAIN LOOP
//*************************************************************

void loop(){

  if (Serial.available()){
    char command = Serial.read();
    byte B[30];
    int i = 0;
    switch (command){            
    case 'e':  // Enable the engine
      engine_active = true;
      previous_millis = millis();
      digitalWrite(ESTOP_RELAY_PIN, HIGH);
      break;

    case 's':  // Stop the engine
      digitalWrite(ESTOP_RELAY_PIN, LOW);
      engine_active = false;
      Serial.println("x");
      break;

    case 'r': // receive new target speed
      while (Serial.available() == 0){
      }
      target_RPMh = Serial.read();
      while (Serial.available() == 0){
      }
      target_RPMl = Serial.read();
      target_RPM = ((long)target_RPMh) << 8 | ((long)target_RPMl);
      break;
      //case ...etc:
    }
  }

  manual_control = true;
  //engine_active = true;
  stopped=false;
  if(engine_active && !stopped){ 
    actual_interval = millis() - previous_millis;            // calculate time pasted since last sample

    if (actual_interval >= SAMPLE_PERIOD/* 200 ms */){            // has enough time passed fo the next sample  
      engine_RPM[0] = (long)((encoder_increments * 500.0) / actual_interval);         //calculate the engine speen in RPM
      engine_torque = analogRead(LOAD_CELL_PIN);       // read the load cell voltage
      encoder_increments = 0;                        // reset the counter
      previous_millis = millis();         // set the sample interval

      Send_speed_and_torque(engine_RPM[0], actual_interval, engine_torque);  //send the newly sampled values      


      //---Automatic Control---------------------------------------------
      if (!manual_control){
        // Adjust Servo
        E_speed = target_RPM - engine_RPM[0];

        P_speed = Kp_speed * E_speed;
        I_speed += (Ki_speed * E_speed * actual_interval);
        D_speed = Kd_speed * (E_speed-E_speed_old)/actual_interval;

        Servo_Dpos += P_speed + I_speed + D_speed;

        if (Servo_Dpos > 179){
          Servo_Dpos = 179;
        }
        else if (Servo_Dpos < 0){
          Servo_Dpos = 0;
        }

        Servo_pos = (int)Servo_Dpos;
        E_speed_old = E_speed;

        throttle_servo.write(Servo_pos);
      }
      //---Manual Control-------------------------------------
      // throttle position is contolled by a single turn pot
      // and torque is adujsted incrementally with a toggle switch
      else {    
        int val = analogRead(LOAD_CELL_PIN);            
        Servo_pos = map(val, 0, 1023, 0, 179);
        throttle_servo.write(Servo_pos);

        if (digitalRead(DYNO_TOGGLE_UP)==0){// && !digitalRead(MOTOR_TOP_PIN))
          digitalWrite(MOTOR_UP_PIN, 1);
          Serial.println("U");
        }
        else if (digitalRead(DYNO_TOGGLE_DOWN)==0){// && !digitalRead(MOTOR_BOTTOM_PIN))
          digitalWrite(MOTOR_DOWN_PIN, 1);
          Serial.println("D");
        } 
        else{ // if motor should not be on make sure it is off
          digitalWrite(MOTOR_UP_PIN, 0);
          digitalWrite(MOTOR_DOWN_PIN, 0);
          Serial.println("O");
        }
      }                        
    }
  }
}


void encoder(){
  encoder_increments++;
}

void Send_speed_and_torque(long rpm, long time, long torq){
  Serial.println('s');      
  printByte(rpm);
  printByte(time);
  printByte(torq); 
}

void printByte(long val){  // sends long as type bytes instead of string
  byte h = (val&~255)>>8;
  byte l = val&255;
  Serial.print(h);
  Serial.println(l);

}

Where is the declaration for encoder_increments?

the data is corrupted when it arrives at the PC

someone suggested to me that increasing the baud rate might reduce the likelihood of of the Serial being interrupted

Which is it? Is the serial data corrupted or interrupted? What do you mean by "corrupted"? What do you mean by "interrupted"?

but if you say this can't happen then it probably won't solve anything

How could I possibly have made a comment about "Serial being interrupted". This is the first time you've mentioned anyting being "interrupted".

Where is the declaration for encoder_increments?

encoder_increments is declared as a long right at the beginning along with all other variables

Which is it? Is the serial data corrupted or interrupted? What do you mean by "corrupted"? What do you mean by "interrupted"?

Like i mentioned i'm sending the data as an identification byte and then 3 sets of two byte each separated by a line feed.

void Send_speed_and_torque(long rpm, long time, long torq){
  Serial.println('s');
  printByte(rpm);
  printByte(time);
  printByte(torq);
}

void printByte(long val){  // sends long as type bytes instead of string
  byte h = (val&~255)>>8;
  byte l = val&255;
  Serial.print(h);
  Serial.println(l);
}

the receiving program then takes the two separate byes and puts them back together into a useful number.

#python code
raw = com.readline()
engine_RPM = byte2int(raw[0], raw[1])

this works fine at low engine speed, at higher speed I start getting Index out of range errors, so three bytes were sent, including the linefeed, but only one was received, which I guessed, means something got lost/stopped/interrupted somewhere along the line.

How could I possibly have made a comment about "Serial being interrupted". This is the first time you've mentioned anyting being "interrupted".

Given the fact that the UART is a piece of hardware driven by the processor's clock, the only way corrupt serial output could be caused by a high interrupt frequency is if there is a serious bug in the hardware. That most certainly is not the case..

I assumed thats what you meant; that a hardware interrupt can interrupt data being sent or received or serial.

westfw said something about the UART being able to overflow, could this occur if the data sending takes too long? and could that in some way cause errors elsewhere, like overflowing into other registers.

encoder_increments is declared as a long right at the beginning along with all other variables

In the code you posted?

this works fine at low engine speed, at higher speed I start getting Index out of range errors

Are you certain the problem doesn't arise when switching from automatic mode to manual mode? Or vice-versa?

Or is the mode changed automatically when the RPM is higher?

In the code you posted?

no my code was too long to fit into the post so I left the declarations out, I can add everything if think it could help.

As far as the type of control goes; the control can only change if I send it a command for the PC, and the errors occur regardless of which one is active...

oooooh... I see why you ask about declarations... I saw now that encoder_increments is volatile. I looked it up a bit, and Arduino reference says:

Specifically, it directs the compiler to load the variable from RAM and not from a storage register, which is a temporary memory location where program variables are stored and manipulated.

I did not know that

Under certain conditions, the value for a variable stored in registers can be inaccurate.

and I remembered that when the data I receive gets skrewy, the PID goes completely out of control as well. So that means that it's the actual value thats wrong and not the communication?

Could this be the source of my problem?

Ok, well I've pushed the baud up to 576 000, decreased the amount of data being sent, and put some code into both program to detect and handle the few errors that do occur. I'm sampling slightly less often.

The control program hasn't freaked out in a while, and the every seems to be working ok for my demonstration on monday.

It's too late to make any changes to the program now but I'd still very much like to know how/why this is happening and if there is any proper solution.