-1

I am dealing with code thatt i received that relies heavily on timer interrupts. i am using arduino Mega (Funduino)

More specifically, when a signal arrives in a pin, an interrupt is run that starts a timer. A different interrupt is run from inside the 1st interrupt, when the timer overflows.

The interrupts measure time between the incoming signal pulses, to figure out the resistance of a sensor, that is being produced from a supporting analog circuit.

The problem is when the sensor doesn't work. For example, the resistance is open circuited. Then the analog circuit does not produce and signal, and no interrupt is triggered.

My code is stuck on the loop() function. It cannot progress because it requires a flag to be set up ,from when the interrupt finishes execution (the interrupt sets that flag at the end).

What i would like to do, is to detect when the resistance is open circuited and move on. This means i have to figure out how to detect when the interrupt is not run.

Perhaps i would set a timer to that flag that the interrupt sets? And if some time passes without the flag being set, this means that the interrupt did not run.

But using another timer (my interrupt already uses two timers) maybe would complicate things further. Any ideas?

Maybe i could also check if the pin that receives the signal does not receive anything for some time... Maybe this would be a better option. If it is, what's the best way to do so, considering the fact i already use two different timers?

This is the code:

/***** CONSTANTS *****/
const int BAUD_RATE = 9600;

const int INTERRUPT_PIN = 3;

const int S0_PIN = 4;
const int S1_PIN = 5;
const int S2_PIN = 6;
const int OE_BAR_PIN = 22;

const int NUM_OF_SENSORS = 8;

/***** ANALOG CIRCUIT CONSTANTS *****/
const double CAPACITOR_VALUE = 400; //pF
const double GAIN_VALUE = 3.5222672065;
const double R_BIAS_VALUE = 98.7; //kΩ

/***** GLOBAL VARIABLES *****/
int number_of_samples = 10;

int pulse_counter = 0;
byte measurement_counter = 0;

unsigned long overflows = 0;
unsigned long ticks = 0;
double period = 0;

boolean interrupts_enabled = false;
boolean measurement_finished = false;

int sensor_coord;

boolean full_mode = false;

void setup() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), count_time_of_k_pulses, RISING);

  pinMode(OE_BAR_PIN, OUTPUT);
  pinMode(S0_PIN, OUTPUT);
  pinMode(S1_PIN, OUTPUT);
  pinMode(S2_PIN, OUTPUT);

  TIMER1_init();

  Serial.begin(BAUD_RATE);
  while (! Serial);
}

void loop() {
  if (Serial.available()) {
    /***** DEFINE MEASUREMENT MODE *****/
    String input = Serial.readString();
    if (input[0] == 'r' && interrupts_enabled == false) {
      if (input[1] == 'f') {
        full_mode = true;
        sensor_coord = 0;
      } else
        sensor_coord = input[2] - '0';

      select_sensor();

      /***** SET VARIABLES FOR DUMMY MEASUREMENT *****/
      number_of_samples = 10;

      pulse_counter = 0;
      measurement_counter = 0;

      TIMER1_clear();

      interrupts_enabled = true;
    }
  }


//THIS IS WHERe I BELIEVE THE CHECK SHOULD OCCUR, OF WHETHER THE INTERRUPT DID NOT RUN. OR WHETHER THE PIN 3 IS RECEIVING A ZERO SIGNAL FOR SOME TIME


  if (measurement_finished) {
    measurement_counter++;

    ticks = (overflows * 65536) + TCNT1;
    period = (ticks * 0.0625) / (number_of_samples - 1);

    if (measurement_counter == 1) {
      /***** SET VARIABLES FOR MEASUREMENT *****/
      number_of_samples = 500;

      pulse_counter = 0;

      TIMER1_clear();

      interrupts_enabled = true;
    }
    else {
      TIMER1_print_results();

      if (full_mode) {
        sensor_coord++;

        select_sensor();

        /***** SET VARIABLES FOR DUMMY MEASUREMENT *****/
        number_of_samples = 10;

        pulse_counter = 0;
        measurement_counter = 0;

        TIMER1_clear();

        interrupts_enabled = true;

        if (sensor_coord == NUM_OF_SENSORS) {
          full_mode = false;
          interrupts_enabled = false;
        }
      }
    }
    measurement_finished = false;
  }
}

/***** INTERRUPT ROUTINES *****/

void count_time_of_k_pulses() {
  if (interrupts_enabled) {
    pulse_counter++;

    if (pulse_counter == 1)
      TIMER1_start();

    if (pulse_counter == number_of_samples) {
      TIMER1_stop();

      measurement_finished = true;
      interrupts_enabled = false;
    }
  }
}

ISR (TIMER1_OVF_vect) {
  overflows++;
}

/***** FUNCTION FOR SENSOR SELECTION *****/

void select_sensor() {
  digitalWrite(OE_BAR_PIN, LOW);
  digitalWrite(S2_PIN, ((sensor_coord & 4) == 4) ? HIGH : LOW );
  digitalWrite(S1_PIN, ((sensor_coord & 2) == 2) ? HIGH : LOW );
  digitalWrite(S0_PIN, ((sensor_coord & 1) == 1) ? HIGH : LOW );
}

/***** TIMER1 FUNCTIONS *****/

void TIMER1_init() {
  TCCR1A  = 0;
  TCCR1B  = 0;
  TIMSK1  = 0;
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
}

void TIMER1_start() {
  TIMSK1 |= (1 << TOIE1);
}

void TIMER1_stop() {
  TIMSK1 = 0;
}

void TIMER1_clear() {
  TCNT1     = 0;
  overflows = 0;
}

void TIMER1_print_results() {
  Serial.println("----------------------------------------");
  Serial.print("SENSOR COORDINATE         = ");
  Serial.println(sensor_coord);

  Serial.print("NUMBER OF PULSES MEASURED = ");
  Serial.println(pulse_counter);

  Serial.print("TIMER1 VALUE              = ");
  Serial.println(TCNT1);

  Serial.print("TIMER1 OVERFLOWS          = ");
  Serial.println(overflows);

  Serial.print("TICKS                     = ");
  Serial.println(ticks);

  Serial.print("MEASURED PERIOD           = ");
  Serial.print(period);
  Serial.println(" US");

  Serial.print("MEASURED RESISTANCE       = ");

  double resistanse = ((period * GAIN_VALUE * 1000) / (4 * CAPACITOR_VALUE)) - R_BIAS_VALUE; //kΩ
  Serial.print(resistanse);

  Serial.println(" kΩ");

  Serial.println("----------------------------------------");
}

EDIT: This is the updated code, with help from chrisl. Only the lines or blocks of code with '!!!!!!!!!!!' are new, so there is not much new code.

/***** CONSTANTS *****/
const int BAUD_RATE = 9600;

const int INTERRUPT_PIN = 3;

const int S0_PIN = 4;
const int S1_PIN = 5;
const int S2_PIN = 6;
const int OE_BAR_PIN = 22;

const int NUM_OF_SENSORS = 8;

/***** ANALOG CIRCUIT CONSTANTS *****/
const double CAPACITOR_VALUE = 400; //pF
const double GAIN_VALUE = 3.5222672065;
const double R_BIAS_VALUE = 98.7; //kΩ

/***** GLOBAL VARIABLES *****/
int number_of_samples = 10;


byte measurement_counter = 0;

unsigned long overflows = 0;
unsigned long ticks = 0;
double period = 0;

//variables for determining if a resistance cannot be measured !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
unsigned long timestamp = 0;
unsigned long measurement_timeout = 5000;  //5 secods is the timeout time
boolean sensor_corruption[7]; //each element of the array is the sensor info. If false, resistance is working. If it's true, resistance is open.
boolean ISR_unavailable = false; //this flag is raised, when the ISR dose not run, because no signal comes from the circuit, because resistance is open circuit
boolean setup_finished = false;


//variables that get changed inside the ISR - thus are declared volatile!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
volatile int pulse_counter = 0;
volatile boolean interrupts_enabled = false;
volatile boolean measurement_finished = false;



int sensor_coord;

boolean full_mode = false;

void setup() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), count_time_of_k_pulses, RISING);

  pinMode(OE_BAR_PIN, OUTPUT);
  pinMode(S0_PIN, OUTPUT);
  pinMode(S1_PIN, OUTPUT);
  pinMode(S2_PIN, OUTPUT);

  TIMER1_init();

  Serial.begin(BAUD_RATE);
  while (! Serial);

  //Initializing all array elements to false!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  for (int i=0; i<=7; i++)
  {
    sensor_corruption[i] = false; // false means it's working ok. true means it's corrupted
  }

  setup_finished = true; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

}

void loop()
{
  if (Serial.available())
  {
    /***** DEFINE MEASUREMENT MODE *****/
    String input = Serial.readString();
    if (input[0] == 'r' && interrupts_enabled == false)
    {
      if (input[1] == 'f')
      {
        full_mode = true;
        sensor_coord = 0;
      } else
        sensor_coord = input[2] - '0';

      select_sensor();

      /***** SET VARIABLES FOR DUMMY MEASUREMENT *****/
      number_of_samples = 10;

      pulse_counter = 0;
      measurement_counter = 0;

      TIMER1_clear();

      interrupts_enabled = true;
    }
  }

// TIMEOUT CODE HERE. TO DETERMINE IF A RESISTANCE IS OPEN CIRCUITED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  if(setup_finished && millis() - timestamp > measurement_timeout)
  {
    sensor_corruption[sensor_coord] = true; //label the resistance (indicated by sensor_coord) as true in the boolean array. this means it's open circuited (no signal from circuit)
    measurement_finished = true;
    interrupts_enabled = false;
    ISR_unavailable = true;
  }




  if (measurement_finished)
  {
    //This does not get executed when the resistance is open circuited
    if (!ISR_unavailable) //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this if statement is the only thing i added here
    {
      measurement_counter++;

      ticks = (overflows * 65536) + TCNT1;
      period = (ticks * 0.0625) / (number_of_samples - 1);

      if (measurement_counter == 1)
      {
         /***** SET VARIABLES FOR MEASUREMENT *****/
        number_of_samples = 500;

        pulse_counter = 0;

        TIMER1_clear();

        interrupts_enabled = true;
       }
    }
    else
    {
      TIMER1_print_results();

      if (full_mode)
      {
        sensor_coord++;

        select_sensor();

        /***** SET VARIABLES FOR DUMMY MEASUREMENT *****/
        number_of_samples = 10;

        pulse_counter = 0;
        measurement_counter = 0;

        TIMER1_clear();

        interrupts_enabled = true;

        if (sensor_coord == NUM_OF_SENSORS)
        {
          full_mode = false;
          interrupts_enabled = false;
        }
      }
    }
    measurement_finished = false;
    ISR_unavailable = false; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    timestamp = millis();  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  }
}

/***** INTERRUPT ROUTINES *****/

void count_time_of_k_pulses() {
  if (interrupts_enabled) {
    pulse_counter++;

    if (pulse_counter == 1)
      TIMER1_start();

    if (pulse_counter == number_of_samples) {
      TIMER1_stop();

      measurement_finished = true;
      interrupts_enabled = false;
    }
  }
}

ISR (TIMER1_OVF_vect) {
  overflows++;
}

/***** FUNCTION FOR SENSOR SELECTION *****/

void select_sensor() {
  digitalWrite(OE_BAR_PIN, LOW);
  digitalWrite(S2_PIN, ((sensor_coord & 4) == 4) ? HIGH : LOW );
  digitalWrite(S1_PIN, ((sensor_coord & 2) == 2) ? HIGH : LOW );
  digitalWrite(S0_PIN, ((sensor_coord & 1) == 1) ? HIGH : LOW );
}

/***** TIMER1 FUNCTIONS *****/

void TIMER1_init() {
  TCCR1A  = 0;
  TCCR1B  = 0;
  TIMSK1  = 0;
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
}

void TIMER1_start() {
  TIMSK1 |= (1 << TOIE1);
}

void TIMER1_stop() {
  TIMSK1 = 0;
}

void TIMER1_clear() {
  TCNT1     = 0;
  overflows = 0;
}

void TIMER1_print_results() {
  Serial.println("----------------------------------------");
  Serial.print("SENSOR COORDINATE         = ");
  Serial.println(sensor_coord);

  //Added the if-else statement, to cover the possibility of open circuited resistance !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  if (sensor_corruption[sensor_coord])
  {
  Serial.println("This resistance is open circuited!!!");
  }
  else
  {
  Serial.print("NUMBER OF PULSES MEASURED = ");
  Serial.println(pulse_counter);

  Serial.print("TIMER1 VALUE              = ");
  Serial.println(TCNT1);

  Serial.print("TIMER1 OVERFLOWS          = ");
  Serial.println(overflows);

  Serial.print("TICKS                     = ");
  Serial.println(ticks);

  Serial.print("MEASURED PERIOD           = ");
  Serial.print(period);
  Serial.println(" US");

  Serial.print("MEASURED RESISTANCE       = ");

  double resistanse = ((period * GAIN_VALUE * 1000) / (4 * CAPACITOR_VALUE)) - R_BIAS_VALUE; //kΩ
  Serial.print(resistanse);

  Serial.println(" kΩ");
  }

  Serial.println("----------------------------------------");
}

And this is the output i get:

----------------------------------------
SENSOR COORDINATE         = 0
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 1
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 2
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 3
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 4
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 5
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 6
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 7
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 8
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 8
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 8
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 8
This resistance is open circuited!!!
----------------------------------------
----------------------------------------
SENSOR COORDINATE         = 8
This resistance is open circuited!!!
----------------------------------------

As you can see, all the resistances are falsely labeled as open circuited. Not only that but for some reason, it measures resistance 8 which does not exist, multiple times. Actually then it loops forever on resistance 8 (Normal operation stops at resistance 7 )

  • Comments are not for extended discussion; this conversation has been moved to chat. – VE7JRO Dec 27 '19 at 19:17
2

In your code you are using the measurement_finished variable as a flag, that a new measurement was finished inside the ISRs, so that it can be handled inside the loop() function. This is very useful. As I understood, an open circuit means, that no measurement can happen, since no pulses are created. So you need to check, how much time passed since the last measurement.

Also the Arduino core sets up Timer0 for general timekeeping purposes. For example the delay() function uses it, and also functions like micros() and millis(), which will return the number of micro/milli-seconds elapsed since startup. So, if you are using these functions, you are actually using a hardware timer, but you don't have to set it up yourself. It is already done by the Arduino core.

So I suggest a principle similar to the BlinkWithoutDelay example from the Arduino IDE. First you define a global timestamp variable and a timeout value (here 1s aka 1000ms):

unsigned long timestamp=0;
unsigned long measurement_timeout=1000;

Then inside your loop() function (in fact you can do this exactly, where you placed the corresponding comment for the check), you check for the difference between the current time and the timestamp:

if(millis() - timestamp > measurement_timeout){

This will trigger, when the timestamp was last updated 1s ago. Inside this measurement, you do, what ever you need to do, if a measurement timeout happens (like printing an appropriate message to Serial). And now, we want to start a new timeout by updating the timestamp variable to the current time, as given by millis(). We could do this inside the corresponding ISR, but that would give us a problem. The ISR could fire right in between of the calculation of our if statement. This cannot happen for single byte variables (as for an 8 bit controller the current 8 bit action will be completed in one step), but unsigned long has 4 bytes. We would need to disable interrupts, when accessing it, to keep it from getting corrupted.

So we can simply set the timestamp inside the

if (measurement_finished) {

statement in the loop function. There we simply do

timestamp = millis();

to update the timestamp. As long, as the if statement is evaluated often enough (at least 1 time during the timeout range), it will work.

So, what happens in the changed code? When you start the code, it might think, that a zero measurement was done (if your setup() function needs longer than the timeout), but as soon, as the first measurement arrives, the if (measurement_finished) statement will execute and update the timestamp. Now the millis() if statement is disabled for the timeout duration, each new finished measurement will reset this timeout. It is like a dead man's switch. The timeout will trigger, when there is no one (or no code) to reset the timeout again.


BTW: As you are using interrupt routines, you should declare all variables, that get changed in an ISR as volatile like this:

volatile int pulse_counter = 0;

If you are not doing this, the compiler might optimize the variable to use a cached value, instead of the real value. Also think about, what I wrote above about variables changed by ISRs in the middle of a calculation. You have to be careful to not run in difficult to debug situations, if you don't protect your variables in the main code against change from an ISR.

  • Thank you very much for your help. I tried your approach, but unfortuantely, for some reason, it gets all my resistances as open circuited. Not only that, but one resistance is measured multiple times. I will edit and post my new code. All the lines that have this character is the ones i added. So not much code is new. – user1584421 Jan 8 at 20:07
  • These characters (!!!!!!!!!!!) – user1584421 Jan 8 at 20:24
  • After further testing, your solution does not work, as it always stays in that block. Instaed of only staying when the interrupt doesnt run. – user1584421 Jan 9 at 18:59
  • Why are you doing interrupts_enabled = false; at a timeout? As I understand your code, that would prevent any new pulses to be measured. Also you set measurement_finished to true, despite you not having a finished measurement. This resets the timestamp in the next if statement, but you don't turn on the interrupts through the variable there. Without the ISR actually measuring pulses, you again run into a timeout. So the interrupt is only reactivated if you send the corresponding serial data. And I don't know, why you habe the ISR_unavailable variable. Don't see, where you would need it – chrisl Jan 9 at 21:05
  • Also I'm agreeing with Edgar, that rewriting your code with an FSM with 1 state variable will lead to a simpler, cleaner and a lot easier to debug code. – chrisl Jan 9 at 21:06
1

I tried to understand your program and found quite a few issues, which I am listing below. This is more a general review than a specific answer. I hope it nevertheless helps you fix the code.

The major issue is the logic of loop() being quite convoluted and hard to follow. My guess is that the issue you face is a direct consequence of this single problem. I won't propose a fix because I can't really understand what this logic is supposed to accomplish. I think this problem is rooted in that logic having too much “state”, i.e. too many global variables that influence its behavior.

You may be able to make the logic of the function a lot clearer if you consolidate various state-defining variables into a single enum variable named state, and write the logic as in a regular finite state machine. This should make it a lot easier to understand, which in turn should make the bugs evaporate like magic.

Now, some specific details in the code.

I can't understand the purpose of measurement_counter. It is used only in the test if (measurement_counter == 1). A boolean named first_measurement may be more appropriate here. Better yet if you can merge it into the unique state variable.

The variable setup_finished has no purpose at all. It is used only in loop() to test whether setup() is done. However, loop() only starts running after setup() is complete, so the test is pointless.

The handling of OE_BAR_PIN looks wrong: the pin goes LOW in setup() and never goes HIGH again. It would seem saner to initialize it HIGH, keep it HIGH when toggling the sensor-select pins, and only turn it LOW once a sensor has been properly selected. Thus:

void setup() {
  ...
  digitalWrite(OE_BAR_PIN, HIGH);
  pinMode(OE_BAR_PIN, OUTPUT);
  ...
}

void select_sensor() {
  digitalWrite(OE_BAR_PIN, HIGH);
  digitalWrite(S2_PIN, ((sensor_coord & 4) == 4) ? HIGH : LOW );
  digitalWrite(S1_PIN, ((sensor_coord & 2) == 2) ? HIGH : LOW );
  digitalWrite(S0_PIN, ((sensor_coord & 1) == 1) ? HIGH : LOW );
  digitalWrite(OE_BAR_PIN, LOW);
}

Note that setup() turns the pin HIGH before setting it as OUTPUT: this prevents an unwanted short LOW pulse.

The handling of Timer 1 is also flaky: it runs continuously. TIMER1_start() and TIMER1_stop() start and stop the overflow counting, not the timer itself. This means the measured tick count can be off by as much as ±65,535 ticks. It would be more sensible to really start and stop the timer as needed:

void TIMER1_init() {
  TCCR1A  = 0;
  TCCR1B  = 0;
  TIMSK1 |= (1 << TOIE1);
}

void TIMER1_start() {
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
}

void TIMER1_stop() {
  TCCR1B  = 0;
}

You probably want to call TIMER1_stop() when you detect a timeout.

  • About the measurement_counter.. The first time you perform a measurement, the data is false. Thats why we do the first measurement with 10 samples, just to get rid of it. The second time we do a measurement, we do it properly with 500 samples. Measurement-counter is the toggle between these two. b) I removed the setup_finished and all related stuff. c) About the OE_BAR_PIN and handling of Timer1, i didn;t quite get your points, but i tested your code and the results are similar so i guess you are probably right. d)My code is still stuck in the bracjket where i detect a timeout and i – user1584421 Jan 12 at 16:25
  • Don't k know why. It just cannot exit that region, whether it receives pulses or not. You also said "You probably want to call TIMER1_stop() when you detect a timeout." How will that play out in my code for timeout detection? – user1584421 Jan 12 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.