Multiple channels interrupts on 32u4 Topboard

I have an application where I am trying to count pulses generated by two separate reed switches (a gas meter and a water meter) using interrupts on an Ind.I/O with a 32u4 topboard.  I have been using the suggested code from Loic from a previous post in this forum and have it woring fine for one meter.  The problem is adding the second meter.  In the previous post Loic mentioned the following:

"If more than 1 channel needs to be detected by the interrupt a small comparison routine can be run inside the interrupt service routine, which compares the status of the channels pre-inerrupt to the current status.

Hope this helps.

Best Regards,

Loic"

I seem to be having difficulty figuring out how to run a comparison routine to tell which channel triggered the interrupt.  Would it be possible to elaborate on how to perform this comparison?

Thank you for your assistance.

 

Keith Barkhau
Keith Barkhau
7
| 1 1 2
Asked on 9/1/16, 10:17 PM
0
vote
4443 Views

As in here: https://industruino.com/forum/help-1/question/multiple-pins-interrupt-128 i asked the same question, but never found an solution for this when using the "high voltage inputs". Since u read the inputs in 32u4 toboard via i2c there is no way reading the whole input register AFAIK. I ended up setting i2c speed to 400khz as mentioned here: https://industruino.com/forum/help-1/question/speed-problem-on-i-o-board-100 , this was just fast enough for me.

Daniel
Daniel
52
| 3 1 1
Answered on 9/4/16, 7:15 AM
1
vote

"There is no way" is a bit extreme! I didn't read the docs but if a register exists and it is readable, you can Always addressing through an I2C library. The point here is mostly how fast does it have to be, and checking the speed of different solutions.

Stefano Giuseppe Bonvini
on 9/4/16, 8:35 AM

I said "AFAIK", somebody may know how to, I just never found out.

Daniel
on 9/4/16, 11:42 AM

ahahah sorry, I thought that was the name of the register, you know, I'm a little outdated!

Stefano Giuseppe Bonvini
on 9/4/16, 11:51 AM

Hello, I think what he means is you should check the state of at least one of the reed switches at the beginning of the interrupt routine to check which one is being switched.
Then based on this decision you can update one or the other.

If timing is an issue, although I thnk that a switch is in the range of milliseconds while at 16 Mhz interrupt response is around 1 microsec, you can use flags in the interrupt routine and have the program react in the main routine.
You can even use counters to take care of multiple interrupt, but actually in my experience you will have to take care of bounces instead, setting a delay before a second interrupt can occur.

Maybe if you post your interrupt routine we can elaborate on that.

DigitalRead is not the only way to read status of the input.

You can directly read the input register and have all inputs in one byte,

Stefano Giuseppe Bonvini
Stefano Giuseppe Bonvini
107
| 4 1 3
Answered on 9/2/16, 6:30 AM
0
vote

Hello. You you said "You can directly read the input register and have all inputs in one byte". I can I do that? Thanks. Rui

Rui barroco
on 10/25/16, 8:34 AM

Hi Keith,

Our initial suggestion for a comparison inside the ISR is indeed not possible; we cannot put a Indio.digitalRead inside the ISR because that is based on the Wire library (I2C) which does not work within an ISR as it uses timers. The suggested solution is to use a flag inside the ISR, and do the comparisons inside the main loop. The flag just indicates that an interrupt has been triggered, nothing more. I have tested this on an INDIO with 32u4 topboard, with pulse signals on 2 digital input channels. This solution works accurately up to about 10Hz on each channel; for higher speeds some interrupts are not counted, probably because there are interrupts happening during the comparison in the main loop. This can be improved by increasing the I2C speed to 400kHz, as discussed here. I modified this settting in the Wire.h library file found in arduino/hardware/arduino/avr/libraries/Wire (this is for 32u4; for 1286 there is another Wire library in the hardware/Industruino folder. With this improvement, i got accurate readings up to 33Hz. For higher frequencies, e.g. 250Hz, my reading was 200Hz.

For pulse generator i used an Arduino Uno with 2 mosfets to create a pulse of 1 milliseconds width, of different frequencies. I am not sure this is 100% accurate so if someone has better equipment, feel free to test and contribute.

Below my sketch, suggestions welcome. The loop only takes care of handling the flag, and after 1 second refreshes the LCD with the counters=frequencies of the past second. It can probably be improved by disabling/enabling interrupts during the comparison and LCD display.

===============================================================

/*  Industruino INDIO interrupt example
 *   for 32u4 topboard
 *   pulse inputs on digital channels CH1 and CH3
 */

#include <Indio.h>
#include <Wire.h>

#include <UC1701.h>
static UC1701 lcd;

int counter1 = 0;
int counter3 = 0;
boolean status1 = false;
boolean status3 = false;
long timestamp = 0;
volatile boolean flag = false;


void setup() {

  pinMode(13, OUTPUT);
  analogWrite(13, 0);

  //  Serial.begin(9600);
  lcd.begin();
  lcd.clear();
  Indio.digitalMode(1, OUTPUT); //  Clear CH1 to LOW
  Indio.digitalWrite(1, LOW);
  Indio.digitalMode(1, INPUT); // Set CH1 as an input

  Indio.digitalMode(3, OUTPUT); //  Clear CH3 to LOW
  Indio.digitalWrite(3, LOW);
  Indio.digitalMode(3, INPUT); // Set CH3 as an input

  // Enable Pin Change Interrupt
  PCMSK0 = (1 << PCINT4);
  PCICR = (1 << PCIE0);

  // Global Interrupt Enable
  sei();
  timestamp = millis();
}

ISR (PCINT0_vect)
{
  flag = true;
}

void loop() {

  if (flag) {                                       // if there has been an interrupt (pin change)
    if (status1 != Indio.digitalRead(1)) {          // has CH1 changed?
      status1 = !status1;                           // then change status
      if (status1) { counter1++;                      // if new status = HIGH, increase counter
      }
    }
    if (status3 != Indio.digitalRead(3)) {
      status3 = !status3;
      if (status3) { counter3++;
      }
    }
    flag = false;
  }

  if (millis() - timestamp > 1000) {             // every second

    lcd.setCursor(1, 1);                          // update LCD
    lcd.print("clock: ");
    lcd.print(millis() / 1000);
    lcd.setCursor(1, 3);
    lcd.print("status   freq(Hz)");
    lcd.setCursor(1, 4);
    lcd.print("CH1");
    lcd.setCursor(60, 4);
    lcd.print(counter1);
    lcd.print("  ");
    lcd.setCursor(1, 5);
    lcd.print("CH3");
    lcd.setCursor(60, 5);
    lcd.print(counter3);
    lcd.print("  ");
 
    timestamp = millis();                       // start new time period
    counter1 = 0;                               // reset counters
    counter3 = 0;
  }

}

Tom
Tom
5675
| 1 1 3
Answered on 9/14/16, 10:22 AM
0
vote

Thanks for the response.  My concern is with the timing.  The pulse is quite short (thus the reason to use an interrupt) and I am concerned that a digitalRead of the pin, even in the first line of the ISR, will be too slow to capture the high state of the pulse.  Have you successfully done this before, or is there a something other than a digitalRead I should employ?

Keith Barkhau
Keith Barkhau
7
| 1 1 2
Answered on 9/3/16, 7:39 PM
0
vote

Your answer

Please try to give a substantial answer. If you wanted to comment on the question or answer, just use the commenting tool. Please remember that you can always revise your answers - no need to answer the same question twice. Also, please don't forget to vote - it really helps to select the best questions and answers!

Ask a Question

Keep Informed

About This Forum

This community is for professionals and enthusiasts of our products and services.

Read Guidelines

Question tools

30 follower(s)

Stats

Asked: 9/1/16, 10:17 PM
Seen: 4443 times
Last updated: 9/14/16, 10:28 AM