LowPowerLab Forum

Hardware support => Moteino => Topic started by: olivio on February 21, 2014, 01:57:15 PM

Title: PinChange interrupt?
Post by: olivio on February 21, 2014, 01:57:15 PM
I tried to use PinChangeInt library (https://code.google.com/p/arduino-pinchangeint/) but it doesn't work. Has someone working example of pinchange int?
Title: Re: PinChange interrupt?
Post by: uChip on February 22, 2014, 12:00:26 AM
Nick Gammon has posted a great write up and tutorial about Arduino interrupts including pinchange on his site.  Here is the URL.

http://gammon.com.au/interrupts

Good luck,
Chip
Title: Re: PinChange interrupt?
Post by: olivio on February 22, 2014, 02:14:12 PM
His example didin't work for Moteino. Which interrupt should I hood with attachInterrupt when I need pinchange interrupt from pins 3-6 ?
Title: Re: PinChange interrupt?
Post by: Felix on February 22, 2014, 10:21:49 PM
D3 is a hardware interrupt so you can use that to wake up the atmega. The others are not. So you can probably use the interrupt library to "attach" to those pins. There's no reason why it should not work except for conflicts with the RFM library which does disable hardware interrupts while servicing the radio interrupts.
Title: Re: PinChange interrupt?
Post by: olivio on February 23, 2014, 09:43:03 AM
It doesn't work on pin 4 either... Any ideas what could be done?
Title: Re: PinChange interrupt?
Post by: Felix on February 24, 2014, 09:43:12 AM
I think you'll need to provide a lot more detail than "it doesn't work".
Title: Re: PinChange interrupt?
Post by: olivio on February 25, 2014, 02:59:04 PM
Well.. I do not get any interrupts for pin change using examples of Nick Gammon for pin 4 (or any other pin either). Could someone else try it?
Title: Re: PinChange interrupt?
Post by: BIA on June 09, 2014, 02:29:28 PM
It doesn't work.

For your additional info, Felix, no interrupts are being raised on PORTC, PORTD or PORTB. Either that or the processor is halting when this happens. When an attempt is made to raise a pin change interrupt, such as with a quad encoder, the processor resets itself. This is not related to a wireless library because it happens when there is no wireless library included in the code. If you want to test it yourself, just use the code from here: http://playground.arduino.cc/Main/PinChangeIntExample.
Title: Re: PinChange interrupt?
Post by: Felix on June 09, 2014, 03:06:57 PM
It does work, otherwise the transceivers would not work ;)
There are 2 external interrupts on the mega328, D2 and D3. D2 is taken by the radio, if used. D3 is free.

If the MCU resets that sounds like a dip below the 2.7V brownout. That should never happen, except if you're starving the regulator on the Moteino, which is ONLY intended to power the Moteino and not much else (though it can provide a total of 250mA). Or a short or high current inrush somewhere in your circuit which destabilizes the Moteino. You may want to check your power supply to make sure it's stable and keep that regulator happy.

Also mind the fact that external interrupts are not the same as PCINTerrupts which are available on almost all the pins but cannot raise individual software interrupts. We are talking about external interrupts here, and there are only 2 on the mega328 like I said.
Title: Re: PinChange interrupt?
Post by: BIA on June 09, 2014, 03:22:41 PM
I understand interrupts extremely well, Felix. I'm not talking about external interrupts. I am referring to PIN CHANGE interrupts that the OP indicated was not working for him. I have no idea why external interrupts would work on D2 & D3 but not on PORTD. Obviously, something is happening as the MCU resets itself when a pin change interrupt is triggered using the pinchangeint library.

Have you tried the pin change interrupt example? If it's working for you then I must have a bad batch of Moteinos. My power supply is fine.
Title: Re: PinChange interrupt?
Post by: Felix on June 09, 2014, 04:47:45 PM
I am sure you understand the interrupts. However what confused me is when you said
QuoteI have no idea why external interrupts would work on D2 & D3 but not on PORTD.
D2 and D3 are indeed on PD2 and PD3.

QuoteIf it's working for you then I must have a bad batch of Moteinos.

I will say that one Moteino having a bad pin is an extremely low possibility. Add D3 as a constraint, and add more Moteinos with the exact same problem and you are nearing negative infinity. Really. They are loaded with the fuses/lockbits/bootloader and tested for RF functionality before shipping. The radio working is proof external interrupts are working.

I wrote this sketch for you to verify the D3 interrupt. Touch a jump wire from 3.3V to D3 and every time you do, you should see the LED blink faster:

boolean interrupt = false;
void blink()
{
  interrupt = true;
}

void setup()
{
  pinMode(LED, OUTPUT);
  attachInterrupt(3,blink,RISING); //external interrupt on D3 of mega328
  Serial.begin(115200);
}

void loop()
{
  if (interrupt)
  {
    Serial.print("X");
    for (byte i=0; i<10; i++)
    {
      digitalWrite(LED, HIGH);
      delay(25);
      digitalWrite(LED, LOW);
      delay(25);
    }
    interrupt = false;
  }
 
  digitalWrite(LED, HIGH);
  delay(500);
  digitalWrite(LED, LOW);
  delay(500);
}
Title: Re: PinChange interrupt?
Post by: BIA on June 10, 2014, 12:05:19 AM
Felix, I think we must have a major disconnect in communication. To be clear: external interrupts (such as they are) are working. There is no issue with external interrupts.

The issue is with pin change interrupts. Of course I understand D2 & D3 are PORTD pins. I am not concerned with 2 & 3. I am concerned with pins that are not 2 and 3.

Your example uses an external interrupt. Of course they work, although your code does not. To make your code work, line 1 must be changed to volatile boolean interrupt = false; and line 10 to attachInterrupt(1,blink,RISING); There is no interrupt 3 on the Moteino that I'm aware of and it doesn't work with the pin number like Due.

With those changes, external interrupts do work. As you pointed out, your wireless would not work otherwise.

However, if you include #include <PinChangeInt.h> in your code and change line 10 to PCintPort::attachInterrupt(4, blink, RISING); (or any other pin) it does not work. If you raise an external interrupt pin (2 or 3) in this program, I believe you will find that the MCU does indeed reset. The halt is being caused by external interrupts while the library is in use.

In all fairness, when I read the fine print of the wiki for the PinChangeInterrupt library, the author says it is only supported on the Arduino Uno and Duemilanove, although he surmises that it "should" work on any ATmega168 or ATmega328-based processor. I can't speak for the other 31 flavors, but I can state unequivocally it does not work on Moteino.

If the OP is still around and concerned about pin change interrupts, I cobbled together this library from some other code that also did not work on the Moteino. This works, at least on mine:


// Pin change interrupt library that works for Moteino.

#include "Arduino.h"
#include "pins_arduino.h"
/* Pin to interrupt map:
* D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
* D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
* A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
*/

volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

static int PCintMode[24];

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = {
  NULL };

volatile static uint8_t PCintLast[3];

/*
* attach an interrupt to a specific pin using pin change interrupts.
*/
void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  }
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  if (port == 1) {
     slot = port * 8 + (pin - 14);
  }
  else {
     slot = port * 8 + (pin % 8);
  }
//
  PCintMode[slot] = mode;
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  }
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      // Trigger interrupt if mode is CHANGE, or if mode is RISING and
      // the bit is currently high, or if mode is FALLING and bit is low.
      if ((PCintMode[pin] == CHANGE
          || ((PCintMode[pin] == RISING) && (curr & bit))
          || ((PCintMode[pin] == FALLING) && !(curr & bit)))
          && (PCintFunc[pin] != NULL)) {
        PCintFunc[pin]();
      }
    }
  }
}

SIGNAL(PCINT0_vect) {
  PCint(0);
}

SIGNAL(PCINT1_vect) {
  PCint(1);
}
SIGNAL(PCINT2_vect) { // for PIND (pins 0-7)
  PCint(2);
}


Just save the code in your libraries as PCInterrupt.h (or whatever you want to call it) and include it in your code. Here is a sample program that uses the library:


// Sample code for pin change interrupt on Moteino. Raising the pin defined by PIN1 increments a counter, raising the pin defined by PIN2 decrements a counter.

#include <PCInterrupt.h>

#define PIN1  4 // pin for the first interrupt (can be any pin except 0 - 3)
#define PIN2  5 // pin for the second interrupt (can be any pin except 0 - 3)

volatile int ticktocks = 0;

void setup() {
  Serial.begin(115200);
  pinMode(PIN1, INPUT); // pin mode must be set to INPUT or the interrupt will not read
  pinMode(PIN2, INPUT);
  PCattachInterrupt(PIN1, tick, CHANGE); // attach the interrupt (CHANGE, RISING, FALLING, etc.) There is also a PCDetachInterrupt function: PCDetachInterrupt(PIN)
  PCattachInterrupt(Pin2, tock, CHANGE);
  delay(2000);
  Serial.println("Ready.");
}

int priorTicktocks = 0;

void loop() {
  if (ticktocks != priorTicktocks) {
    Serial.println(ticktocks);
    priorTicktocks = ticktocks;
  }
}

void tick(void) { // interrupt on PIN1
  ticktocks++;
}

void tock(void) { // interrupt on PIN2
  ticktocks--;
}
Title: Re: PinChange interrupt?
Post by: Felix on June 10, 2014, 10:29:15 AM
Yeah that felt like a disconnect. I have not used PCINTs myself much at all. I am more concerned with external interrupts since my devices make use of those. I can't speak much for pin change interrupts, but I know you have to look at a register to determine which pin actually caused the interrupt after it happens, as opposed to the external interrupts which have direct routines attached to them and it's obvious. I think your library is attempting to do just that but I have not tried it.
There is no reason why the chips would not work as advertised by Atmel. I do not modify the chips in any way that should affect the functions of those PCINT pins. In such cases it's typically the code that is bad not the chip hoping this doesn't sound defensive.
Title: Re: PinChange interrupt?
Post by: BIA on June 10, 2014, 12:06:48 PM
As we all know, not everything that works on a chip works on all implementations of that chip. It appears the PinChangeInt library is very limited in its portability.

However, pin change interrupts are even more critical on the Moteino than other implementations since it is operating with one hand tied behind its back, so to speak, with one of only two external interrupts dedicated to wireless. That chip is woefully short in interrupts to begin with.

Hopefully, Felix, you will get your implementation of the ATmega1284P working. From what I can tell, it has 3 external interrupts?? Not much, but at least it puts the platform back on level footing (interrupt-wise) with other 328P-based platforms. BTW, Felix, I read your post on your progress with the ATmega1284P. There is an issue with interrupts on that chip (or with Arduino libraries, depending on which you want to blame) that causes software serial not to work correctly. The issues you are having may (or not) be related to this. The 2560P would be a much better chip for a mega IMO.

I just put that other library together in haste, yesterday. At this point I can only say that it will detect interrupts on any pin > 3, and is surprisingly fast, however, I can't say how accurate it is. I plan to try it with an encoder and see how well it works. If I find an issue, I'll post it.
Title: Re: PinChange interrupt?
Post by: Felix on June 10, 2014, 12:21:05 PM
Thanks for the follow up.
On the 1284P - I chose this chip not knowing much about it other than it has a wealth more features and IO than the 328, and just started playing with it once I got my Moteino MEGA prototype PCBs. Yes, just 3 interrupts unfortunately, but another hardware UART which comes in very handy, and bunch more memory in all areas. I assumed the interrupts and 3.3V 16mhz "overclocking" would work the same as the 328. Unfortunately that is not the case. Unless I provide 5V the chip will stall at 16mhz, something I've never seen on the 328, not even once. The external interrupts from the RFM69 radio are also not working, but do work on the RFM12B.
The mega2560 is double in price and more massive to integrate in such a small board (there's the arduino mega for such purposes).
It's sad that atmel doesn't offer a larger 8bit chip that runs at at least 16mhz at 3.3V. They have other lines of chips, the problem is they are not ported to the Arduino environment. People want cheap, quick easy to use, a difficult combination to meet.
Title: Re: PinChange interrupt?
Post by: BIA on June 10, 2014, 01:40:16 PM
I agree wholeheartedly. This chip, MK20DX256, http://www.mouser.com/ProductDetail/Freescale-Semiconductor/MK20DX256VMC7/?qs=6ddF3R/6EV8uWVNeTG2uUQ==, is compatible at 3.3v and 5v. I don't know if you could integrate it with the RFM chips but it would be an awesome product if you could. You would have to modify the standard Arduino IDE and bootloader but it would be well worth the effort. The price about the same as the 1284P.
Title: Re: PinChange interrupt?
Post by: Felix on June 10, 2014, 02:18:57 PM
You know that looks like a great chip, but most people are having a hard time using anything but the Arduino IDE. I know some of the more expert folk use AVR studio and other environments but they are the few exceptions. I can't even imagine what effort it would take to build a dev board around such a chip (BGA too :)), price it right, and make it profitable and worth it. I am trying to have a few products that make sense, are easy to use, reasonably priced, and address 99% of people's needs. Adding the 1284 or similar chip would solve another 0.5% of needs. There are other cortex chips that are not BGA, but nobody that I know of has ported such a core to the Arduino IDE with proper documentation. I think the Arduino folks should look into moving towards cortex chips that can run 72mhz at 3.3V or less, and dump atmel's lazy attitude, limited set of chips and ultra inflated prices.
Title: Re: PinChange interrupt?
Post by: BIA on June 10, 2014, 03:17:40 PM
Agreed. Atmel has a lazy attitude and limited chipset because they have a stranglehold on the Arduino/hobby marketplace. The only way that is going to change is if more integrators start using other chips such as the Freescale chips. Not holding my breath that the "Arduino folks" are going to do anything.
Title: Re: PinChange interrupt?
Post by: KanyonKris on June 10, 2014, 08:13:23 PM
Adding ARM to Arduino has been done. The Teensy 3.0 and 3.1 boards with Teensyduino.

https://www.pjrc.com/teensy/teensy31.html

https://www.pjrc.com/teensy/td_download.html

Perhaps you could work with Paul (the Teensy engineer/owner) to make a Teensy with a RFM69. From my reading in the forums Paul seems like a nice guy. Sadly his designs have been copied by others when they could have contacted him to distribute his boards, but I guess that's what comes with open source. Still, I think it would be courteous to contact Paul first before going it on your own.
Title: Re: PinChange interrupt?
Post by: Felix on June 11, 2014, 08:31:15 AM
Teensy is cool and v3 even cooler. But it was always only almost open source. There is only a schematic, some documentation on the bootloading process. I have a teensy v2 but not a v3 but looks like it's the same. I know he's using halfkay and he has an application to load the binary. I haven't watched his progress up close and I checked again today but still there are no source files or anything that would qualify it as open source. Nothing wrong with that, he is filling a niche gap which is great.
Right now staying focused and releasing new products while improving existing is very very hard. Partnerships almost always suck and end up in regret and lost time and dismal/zero profit. I am building a new version of the Moteino MEGA that addresses the issues I've had. If ARM becomes mainstream a Moteino ARM would be cool ;)
Title: Re: PinChange interrupt?
Post by: Lensdigital on November 17, 2014, 01:42:09 PM
I've recently experimented with Moteinos (both R2 w/ RFM12b and R3 w/RFM69) powered by coin cell battery and in my case pinChangeInt library worked extremely well, and I saw no issues at all (to my surprise). My goal was to wake Moteino from "deep sleep" by pressing multiple buttons (thus D3 was not enough). In my cases I tested it with 3 buttons connected to various pins (I think I tried it with most of Analog pins and D4).  There was no conflict with radio module.
Title: Re: PinChange interrupt?
Post by: Felix on November 17, 2014, 07:04:15 PM
That's really great, thanks for the results follow up.
Any code samples you could perhaps share for the community or others that might be interested in trying the same?
Title: Re: PinChange interrupt?
Post by: Charly86 on November 20, 2014, 05:58:37 AM
Hi There,

In my RFM12B library (based on Felix's one) I used both (Hardware IRQ0 and IRQ1) but also Pinchange

take a look on there https://github.com/hallard/RFM12B_arssi in .cpp source code, the refering function is called ConfigureInterrupts

if you define #PINCHG_IRQ then it will use the pinchange mode, then you need to define #RFM_DEFAULT_IRQ with a value from 0 to 14 to use PCCINT0 to 14

It was working fine when I've done tests.



Title: Re: PinChange interrupt?
Post by: Lensdigital on November 24, 2014, 02:53:25 PM
Quote from: Felix on November 17, 2014, 07:04:15 PM
That's really great, thanks for the results follow up.
Any code samples you could perhaps share for the community or others that might be interested in trying the same?
Sketch attached.
I'm using 2 buttons for it.