ATMega328p/Arduino Interupt enabled pins?


  • Hardware Contributor

    Good morning to you all!

    This morning I've been doing some board routing for my current node project, build threat/topic/log located here, and for Revision2 of the board I would like to use interupt enabled pins for my switches. This would allow me to put the device to sleep, saving power consumption from the coin cell battery, then allowing it to be woken upon pin state switch (through a physical switch).

    Now, looking through the datasheet for the ATMega328p It came to my attention that I'm not completely sure on two things in this concern:

    • Difference between a INT pin and a PCINT pin.
    • Am I 'allowed' to use a switch to interrupt the sleep cycle?

    So, being unsure about this, I found a thread on AVRFreaks forum describing this a little. Then it became apparent that in reality I knew nothing about Interrupt enabled pins.

    AVRFreaks Thread Link

    One guy goes on to say that PCINT pins work in groups, you can wake the device with any of these pins but the device will not know which one has changed its state. Does this mean i would have to have on board memory to hold the state of the pins as the chip goes into sleep mode? If so, that would mean that i only have the two pins of INT1 and INT2 to use a stable Interupt pins for my switches, however the nRF uses INT0, so that leaves one pin left. I have 2/3 switches.

    Could someone please shine some sort of light onto this for me 🙂

    Thanks,
    Sam.


  • Mod

    Holding state just means storing it in a variable. You don't need additional hardware.

    http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html Has some more explanation and an example how to do it without state, but assumes that pins are only active briefly. It would probably need debouching and will keep printing if you hold down a switch, so keeping state is likely easier.

    An (even more technical) example is available at http://playground.arduino.cc/Main/PcInt


  • Hardware Contributor

    The part that going my brain ticking was on that AVRFreaks forum i linked, someone stated that it shouldn't really be used for switches because of debounce. Do you feel that as long as debounce (hardware or software) would make it acceptable to use switches on these pins?

    I will be reading those two links you provided on my lunch break at work. Thank you for sending them over.

    Sam.


  • Hardware Contributor

    Debounce can be handled in the code with a gw.wait (500) before going to sleep.


  • Hardware Contributor

    @samuel235 The MySensors library does not use INT0 for the NRF24, so there are in fact 2 pins available. If your board connects the NRF24 interrupt pin (8) with D2, then of course you can not use the pin for a switch anymore. But on a node which sleeps until a switch is tripped, there is no point in connecting the INT of the NRF24 with D2, as the radio will also sleep most of the time.
    The two pins D2 and D3 can wake up the ATMEGA328 from it's deepest sleepstate, which is the one you go into when using the call gw.sleep(0).
    I use sleep(0, LOW, 1, LOW, 0) when going to sleep. Both D2 and D3 are pulled up with 10M resistors, the reeds switches pull these pins low when contact is made (mine are of the normally open type and close when the magnet is moved away from them) to wake up the processor.


  • Hardware Contributor

    @sundberg84 - Use this feature along side of the normal software debounce method in which the mySensors library uses, or am i getting mixed up with the method it does use, does it use the normal arduino method of software debounce or is it using the gw.wait methid you state to use?

    @GertSanders - Right okay, i connected my nRF to the INT pin even though the mySensors doesn't use it. I forgot that it doesn't actually require it. I could remove that then since it is a node that isnt going to be used as a repeater at the moment. Am i correct in assuming that theoretically the IRQ is not needed to be connected since it wont be receiving data on the RF module that would require the uC to wake?

    For my knowledge sake, use a sleeping repeater node for example, would connecting the RF IRQ to INT0 enable the repeater node to sleep until receiving data from another node? Again, i'm not using this method, just attempting to widen my knowledge.


  • Hardware Contributor

    It works the same way, create a delay between readings to avoid getting the false debouce values.


  • Hardware Contributor

    @sundberg84 I can use either way then? I think I'll go down the route of using the gw.wait (500). Any reason to use the other way over this way?


  • Hardware Contributor

    Good thing with gw.wait is that it calls process so if you got an incoming message you dont miss that. Or you can use the normal software debounce and check millis() and dont do a reading for the first 250-500 milliseconds... its the same i think because process is called then. What you dont want to use is delay().


  • Hardware Contributor

    @sundberg84 said:

    What you dont want to use is delay().

    Thank you for making aware of this. I will be using gt.wait (500) I think.

    Back onto the subject of the interupt pins, so do you advice i use INT0 and INT1? I have potentially 3 switchs to put into this node, so i could do with another one. What needs to be done for the PCINT pins, like I said earlier, the state needs to be saved while it sleeps. I will be saving the state of the pin anyway for the switch to work, surly? My switch will be a normal light switch push-on push-off style (Just one button that you depress to toggle the light). Either way, if I monitor the switch in terms of saving the switch state to a variable each time it is changed then surly i would be good to just use any PCINT enabled pins?

    I'm assuming the above based on the extract of the following from the AVRFreaks forum thread I posted earlier.

    "While on modern AVRs most (all?) IO are PCINT. So there's far more interrupt sources but you cannot specify when the interrupt occurs. It just happens on every low-high or high-low transition so you have to keep track of the previous state of pins to know which way it just changed. Also they are grouped in 8's so all 8 pins on a PORT just cause a single PCINT vector/handler to be called. It's then up to you to work out which of the 8 caused the change.
    Oh and whatever else you do, do not make the mistake of using them to read switches/buttons. That's a mistake almost all beginners make as they don't know about contact bounce.
    "


  • Hardware Contributor

    @samuel235 If a node goes into sleep mode using gw.sleep(0), then the radio is powered of as well. So the radio will NOT be able to wake the node via INT.

    There are ways to put the processor to sleep and keep the radio alive, but that would eat the battery very quickly. The Mysensors lib does not provide for this scenario at this moment.

    Maybe someone can extend the MySensor SLEEP function with an additional parameter to indicate the type of sleep (all peripherals, only processor and I2C, only processor and SPI/radio, only processor).


  • Mod

    I like that idea.

    For the ATmega328, sleeping the mcu saves about 15mA, which is on the same level as the nrf in receive mode (14mA). So such a mode would "only" double the battery life. Other mcus (ESP8266?) might have larger differences though, resulting in higher gains.


  • Hardware Contributor

    Ahh okay, thats useful information to know. Thank you @GertSanders!

    I'm currently having a read through this PDF, more specifically page 15 reads:

    External Interrupts
    Pins:
    INT0 and INT1 – range of event options
    INT0 – PORT D [2]
    INT1 – PORT D [3]

    PCINT[23:0] – any signal change (toggle)
    PCINT[7:0] – PORT B [7:0]
    PCINT[14:8] – PORT C [6:0]
    PCINT[23:16] – PORT D [7:0]

    Pulses on inputs must be slower than I/O clock rate

    So, would you advise me attaching the 3 switches to pins INT0, INT1 and any pin on that port B range. Then if i ever needed 4 switches, I could then go to any pin in port C range. Because the PCINT pins are inside of different port ranges from each other, as long as save the state of the switch/pin in a variable I should have no issue with it waking the uC up when the switch is toggled?

    I hope i'm understanding your information correctly guys 🙂


  • Hardware Contributor

    hi.
    just want to add my piece too 🙂
    just a little OT about waking up with radio int. For nrf, you can't as it's power hungry , but for rfm69 you can. Guys at lowpowerlab have already made it, and it seems to consumes uA. but it is not implemented in Mysensors yet. It is a matter of RX ON/OFF timings and ATC Rssi. It is one thing I am interested in, and is on my big todolist 😣

    For PCINT, you understand right if I understand you well too lol!
    For PCINT, I have already played with this for my knowledge months ago. I did the same way @mflakvidd has linked (by playing directly with registers and setting/checking bits).
    And yes, it would be better to make a software debounce. Something like:
    -set your interrupts

    • go to sleep
    • if wake up: in interrupt routine, save state or what you want. check the interrupt "mask" meaning check the bit according to your pin in the right register. disable PCINT on your pin. Disable PCINT for your pin only could be in case your isr could be re-fired.
    • then in your main loop, just after your gw.sleep, do a software debounce like guys advised you. Use millis or gw.wait for instance easier and test state pin. It is better to do this there, not in isr, your isr has to be short (you don't want to spend time in an interrupt)
    • then re-enable PCINT and go to sleep.

    Like you are reading in docs, you will need to handle the pin state. For using registers, it is just a matter of setting the right bits to enable or disable, and in your isr routine checking the right bits to know which pin has fired. That's all. I would advise you to test this in a separate sketch (not a mysensors one) to debug easier (fo instance testing some online already made sketch to see how it works for you.

    My tests were not mysensors related, so it's useless I give you dirty things as others already gave you good links. I began with examples too 😉
    Another docs for interrupt, but maybe you already read it:
    http://www.gammon.com.au/interrupts
    http://www.gammon.com.au/forum/?id=11091


  • Hardware Contributor

    @scalz - Thank you for this information.

    I'm not sure I completely get your example method, however I will do some reading of those links you provided at some point today when I have a spare 5 minutes at work, hoping to futher understand how you meant certain things.

    When you say "Set your interrupts" i'm assuming you meant that I need to assign the interrupt pins:

    digitalPinToInterrupt(2)
    digitalPinToInterrupt(3)
    

    Is this what you meant?

    I will then need to have two variables saved, say sw1 and sw2, write the state of the respecting pins to those variables.
    Once i have done that, I then send the node to sleep.

    What do you mean by disable PCINT? (sorry)
    Perform a gw.wait(500)
    Enable PCINT
    Then go back to sleep.

    Did i understand that process correctly?

    I will be performing all of this on a arduino to make sure i understand the processes and the ordering of what to do what, then I'll get it attached to Rev2 board, once i have finished designing and ordering the PCB that is.


  • Hardware Contributor

    @samuel235: maybe I should have said "init your interrupt" or attach it. it was late lol. It was related to the External interrupt pins(PCINT). It seems digitalpinToInterrupt is new to me, I didn't know it, I used to attachInterrupt and registers when needed. I will look at digitalPintoInterrupt.
    So when I said disable PCINT I was meaning to detach it in the interrupt routine and do your debouncing in loop.

    Yep you have understood the global process. of course you will need to run some tests as what I said is not complete I think, just a way to go.

    • init/ attach your interrupt(pin x)
    • sleep mode
    • on wake up, you will be in your ISR (interrupt service routine). Save state and detach your interrupt for the concerned pin.
    • after the isr is executed, you will be redirected just after the gw.sleep of course. So after this line, do your debouncing by testing pin state for instance, with millis and gw.wait. Maybe you will need to adapt the delay you check pin state with millis because debouncing will depend on your switch. But this should not be a big delay, maybe 5 to 20ms, maybe more it depends on your switch. When I say delay, it is not delay() of course! but the time you use millis()

  • Mod

    digitalPinToInterrups just translates the pin ID to something useful for attachInterrupt. It does nothing when used alone. See
    https://www.arduino.cc/en/Reference/AttachInterrupt


  • Hardware Contributor

    @mfalkvidd: thx 😉 I am already reading it, too curious 🙂


  • Hardware Contributor

    @scalz - Right okay, i think i understand this pretty clearly now on the method to go about enabling interrupt to wake the uC.

    I have just realized that there is a example sketch that uses sleep function for 2 switches, i can use this and modify it to work for 3 when needed. I shall be studying this sketch along with your links later on tonight hopefully.

    Once i have sent off my deigns to get my Rev2 remade i will focus my attention to making sure i completely understand the method of this sleep with interrupt function.

    Thank you for your help guys!


Log in to reply
 

Suggested Topics

  • 87
  • 5
  • 7
  • 6
  • 10
  • 3

59
Online

11.4k
Users

11.1k
Topics

112.7k
Posts