Hey guys, I'm starting to loose hope that the Moteino is going to do what I need it to do. I have a mote with a RFM69HCW and flash chip so I'm beginning to think I should have not gotten the flash chip installed. I need 2 interrupts. I managed to get the pinchangeinterrupt library working on pin 4 for my rain gauge reed switch, but have failed to get another interrupt working for the windspeed which is also a reed switch activated device. I confirmed that the circuit I have for the anemometer is indeed causing the 3.3v to drop to GND when it is briefly shorted to simulate the reed switch. I have tried using pin 3 with the attachinterrupt command without success. I then tried various available digital pins with the pinchangeint command with the same result. I'm obviously missing something here. I have to admit I'm not as well versed with the arduino world as I was with the particle photon which I have used exclusively up until now. I was looking to use the Mote in hopes that it would be less power hungry, but the lack of interrupts available seems to not suit my needs.
Rather than posting my code, can someone post a very basic example of a sketch that uses the pinchangeint library to have two independent interrupts, or a combination of the external pin 3 interrupt and a pinchangeint using say pin 4 on the Mote?
For pin3 I would just use hardware interrupts, sounds like that's what you're trying. That should be very easy and straight forward to use, it's how the RFM69 is using pin2 (int0 hardware interrupt).
Note when you attach you have to use the hardware interrupt number ie for D3 it would be attach(1, handler).
I believe pinchangeint examples/lib/links were already posted in the forum, I suggest searching for "pinchangeint" in the root of the forum.
I know it is a bit hard to look at because it is so dense and obscure but I have developed some macros to control pins and one of the functionalities I just added was the ability to set pin change interrupts on *ANY* digital pin. Something to keep in mind when you use this technique is there is only one interrupt service routine (ISR) for each port so when any pin on portd changes, ISR (PCINT2_vect) is called. The common way to handle this is the first time in that routine you cache the state of the pins on that port. Next time you find yourself there, you can compare to the cached value to see which pin changed state. Hope this can get you started.
DENSE bit banging macros
#if defined(__AVR_ATmega328P__) // Macros to define the ports and bitmasks for the pins on the 328p succinctly
#if F_CPU==8000000 // No external oscillator on PB6 and PB7 - make them digital pins 20 and 21 respectively
#define InpRegFromPin(pin) pin<8 ? 0x09 : pin<14 ? 0x03 : pin<20 ? 0x06 : 0x03 // Input registers for the ports
#define DirRegFromPin(pin) pin<8 ? 0x0A : pin<14 ? 0x04 : pin<20 ? 0x07 : 0x04 // Data direction registers for the ports
#define DataRegFromPin(pin) pin<8 ? 0x0B : pin<14 ? 0x05 : pin<20 ? 0x08 : 0x05// Output registers for the ports
#elif F_CPU==16000000 // External oscillator on PB6 and PB7 - leave them alone
#define InpRegFromPin(pin) pin<8 ? 0x09 : pin<14 ? 0x03 : 0x06 // Input registers for the ports
#define DirRegFromPin(pin) pin<8 ? 0x0A : pin<14 ? 0x04 : 0x07 // Data direction registers for the ports
#define DataRegFromPin(pin) pin<8 ? 0x0B : pin<14 ? 0x05 : 0x08 // Output registers for the ports
#endif
#define BitmaskFromPin(pin) pin<8 ? 1<<pin : pin<14 ? 1<<(pin-8) : 1<<(pin-14)
#endif
// Bit-banging macros - each adds 2 bytes of sketch size and takes 1 clock cycle to execute
#define MakeOutput(pin) _SFR_IO8(DirRegFromPin(pin)) |= BitmaskFromPin(pin) // Much faster and smaller version of pinMode(Pin, OUTPUT)
#define MakeInput(pin) _SFR_IO8(DirRegFromPin(pin)) &= ~(BitmaskFromPin(pin)) // Much faster and smaller version of pinMode(Pin, INPUT)
#define PullHigh(pin) _SFR_IO8(DataRegFromPin(pin)) |= BitmaskFromPin(pin) // Much faster and smaller version of digitalWrite(Pin, HIGH)
#define PullLow(pin) _SFR_IO8(DataRegFromPin(pin)) &= ~(BitmaskFromPin(pin)) // Much faster and smaller version of digitalWrite(Pin, LOW)
#define ReadPin(pin) _SFR_IO8(InpRegFromPin(pin)) & (BitmaskFromPin(pin)) ? 1 : 0 // One line if else statement using the format [test ? true return : false return]
#define DigitalWrite(pin, state) state ? PullHigh(pin) : PullLow(pin)
#define PinMode(pin, mode) mode ? MakeOutput(pin) : MakeInput(pin)
// ========================================= Pin Change Interrupt Macros =========================================
#define PCINTFromPin(pin) pin<8 ? 0x02 : pin<15 ? 0x01 : 0x00
#define PCMaskFromPin(pin) pin<8 ? 1<<pin : pin<14 1<<(pin-8) : 1<<(pin-14)
#define PCI_Reg(pin) pin<8 ? 0x6D : pin<14 ? 0x6B : 0x6C
#define PCI_FR_Msk(pin) pin<8 ? 1<<2 : pin<14 ? 1 : 1<<1
#define PCI_Setup(pin) (*(volatile uint8_t *)((PCI_Reg(pin)) )) = BitmaskFromPin(pin); PCIFR |= PCI_FR_Msk(pin); PCICR |= PCI_FR_Msk(pin); //_SFR_IO8(PCI_Reg(pin)) |= BitmaskFromPin(pin); PCIFR |= PCI_FR_Msk(pin); PCICR |= PCI_FR_Msk(pin)
// Moteino macros
#define REG_OPMODE 0x01
#define SLEEP_RADIO writeReg(REG_OPMODE, 0x00)
#define SS_PIN PB2 // Slave select pin
#define SCK_PIN PB5 // SPI clock pin
#define MOSI_PIN PB3 // Master out slave in (MOSI) pin
// ============================== SPI Definitions ==============================
#define SELECT noInterrupts(); PORTB &= ~(1<<SS_PIN)
#define UNSELECT SS_WRITE_HIGH; interrupts()
#define SS_WRITE_HIGH PORTB |= 1<<SS_PIN
#define WAIT_WHILE_SPI_BUSY asm volatile("nop"); while (!(SPSR & 1<<SPIF))
#define SLEEP_FOREVER cli(); \
SMCR = bit(SM1) | bit(SE); \
MCUCR = bit (BODS) | bit (BODSE); \
MCUCR = bit (BODS); \
sei(); \
__asm__ __volatile__ ( "sleep" "\n\t" :: );
void writeReg(uint8_t addr, uint8_t value) { // Level 1 code - interact witht the radio's registers
SELECT;
SPDR = ( addr | 0x80 );
WAIT_WHILE_SPI_BUSY;
SPDR = ( value );
WAIT_WHILE_SPI_BUSY;
UNSELECT;
}
Main code
#define LED 14 /* ADC0 */
#define INT 4 /* Pin 4 on the Moteino */
int main(void) {
SLEEP_RADIO; /* 30 bytes and 6uS; fastest sleep in the West! */
PinMode(LED, OUTPUT);
/* Setup a pin change interrupt */
PinMode(INT, INPUT);
DigitalWrite(INT, HIGH);
PCI_Setup(INT);
sei();
while (1) {
SLEEP_FOREVER; /* Sleep forever - only awake in the ISR */
}
}
ISR (PCINT2_vect, ISR_NAKED) { /* handle pin change interrupt for D0 to D7 here */
DigitalWrite(LED, HIGH);
_delay_ms(10);
DigitalWrite(LED, LOW);
}
Run this code and the proc will sleep the radio and then go to sleep for all eternity. Unless you use a jumper wire to touch pin 4 to GND. Then you will see that the LED blinks once . When you let pin 4 go high again the LED will blink again. If you measure the current with a uCurrent, you will see your Mote is in deeeeeeep sleep only drawing 6uA. Oh and all this code fits in a crazily compact 224 bytes because that is just the way I roll!
Just to double check, I changed my definition of INT to 5 and as expected now the LED only blinks when the voltage on pin 5 changes. You will need to save states to figure out if pin 4 or 5 get you to the PCINT2_vect but other than that, problem solved.
Quote from: raenrfm on October 05, 2017, 11:15:53 PM
Hey guys, I'm starting to loose hope that the Moteino is going to do what I need it to do. I have a mote with a RFM69HCW and flash chip so I'm beginning to think I should have not gotten the flash chip installed.
What leads you to this conclusion?
Quote
I need 2 interrupts. I managed to get the pinchangeinterrupt library working on pin 4 for my rain gauge reed switch, but have failed to get another interrupt working for the windspeed which is also a reed switch activated device. I confirmed that the circuit I have for the anemometer is indeed causing the 3.3v to drop to GND when it is briefly shorted to simulate the reed switch. I have tried using pin 3 with the attachinterrupt command without success. I then tried various available digital pins with the pinchangeint command with the same result. I'm obviously missing something here. I have to admit I'm not as well versed with the arduino world as I was with the particle photon which I have used exclusively up until now. I was looking to use the Mote in hopes that it would be less power hungry, but the lack of interrupts available seems to not suit my needs.
Rather than posting my code, can someone post a very basic example of a sketch that uses the pinchangeint library to have two independent interrupts, or a combination of the external pin 3 interrupt and a pinchangeint using say pin 4 on the Mote?
If you're going to use pinChange interrupts on a device that also relies on a library that uses the standard interrupt pins (RFM69 library, for a random example), I recommend that you use the EnableInterrupt library.
However, even then it's not intuitively obvious that you need to include one or more MACRO definitions, depending on your code structure. In your sketch file, you need to do this:
// See https://github.com/GreyGnome/EnableInterrupt and the README.md for more information.
#define EI_NOTEXTERNAL // <===== THIS IS NECESSARY because the RFM69 library uses standard interrupt pin INT0, this tells the EnableInterrupt 'library' to not define INT0 or INT1
#include <EnableInterrupt.h>
#include <RFM69.h> // from https://github.com/LowPowerLab/RFM69
#include <SPI.h>
Here is a simple sketch using two external pins and includes the RFM69 library (although doesn't use the radio for simplicity).
// EnableInterrupt Simple example sketch. Demonstrates operation on a single pin of your choice.
// See https://github.com/GreyGnome/EnableInterrupt and the README.md for more information.
#define EI_NOTEXTERNAL
#include <EnableInterrupt.h>
#include <RFM69.h> // from https://github.com/LowPowerLab/RFM69
#include <SPI.h>
// Modify this at your leisure. Refer to https://github.com/GreyGnome/EnableInterrupt/wiki/Usage#Summary
#define ARDUINOPIN 5
#define ANOTHERPIN 6
volatile uint16_t interruptCount=0; // The count will go back to 0 after hitting 65535.
volatile uint16_t otherInterruptCount=0; // The count will go back to 0 after hitting 65535.
void interruptFunction() {
interruptCount++;
}
void otherInterruptFunction() {
otherInterruptCount++;
}
void setup() {
Serial.begin(115200);
pinMode(ARDUINOPIN, INPUT_PULLUP); // See http://arduino.cc/en/Tutorial/DigitalPins
enableInterrupt(ARDUINOPIN, interruptFunction, CHANGE);
pinMode(ANOTHERPIN, INPUT_PULLUP);
enableInterrupt(ANOTHERPIN, otherInterruptFunction, CHANGE);
}
// In the loop we just display interruptCount. The value is updated by the interrupt routine.
void loop() {
Serial.println("---------------------------------------");
delay(1000);
Serial.print("Pin was interrupted: ");
Serial.print(interruptCount, DEC);
Serial.println(" times so far.");
Serial.print("Other Pin was interrupted: ");
Serial.print(otherInterruptCount, DEC);
Serial.println(" times so far.");
}
Finally, I'm including a zip file with this simple example, but modified to show how to use EnableInterrupt in an external module. Note the extra MACRO
#define LIBCALL_ENABLEINTERRUPT
Which is necessary in this case...
Tom
Thanks Tom, yes, with all my searching it was not very apparent that you needed all this "extra stuff" to make this work, nor was it very clear in all my googling that the native hardware interrupts could co-exist with the pinchange interrupt libraries. I guess sometimes when your deep into something like developing a library, what seems obvious to you may not be to someone else (like a newbie like me, well newbie to anything other than the particle photon platform).
Again thanks for spending the time to teach me this stuff. I'll try this and let you know how it turns out.
This may be a dumb question, but where does the mOtherFunctions.h and .cpp go? Compiler blows up with the include statement.
Quote from: raenrfm on October 08, 2017, 11:51:53 AM
This may be a dumb question, but where does the mOtherFunctions.h and .cpp go? Compiler blows up with the include statement.
they would go in the same folder as the sketch. They're considered 'local' files due to the #include using double quotes instead of '<...>'.
Tom
NVM, forgot to restart the IDE before compiling. Looks like I got it working. Thanks for the effort guys. Sometimes learning this stuff is painful...lol.