faster, leaner input polling using PCINT0 flag

Thanks for trying to help. Really. But I'd rather discuss my code with someone who has actually read it. I'm sure we could have an interesting discussion about it but at this point you clearly don't understand what it does or how. I'm happy to explain details but there's not much point if you aren't keen on reading and understanding it. It's not that hard to understand, lots of comments makes it look bigger than it is, it will help to have the datasheet handy.

If it helps I listed a much more concise implementation of this method in post #7. You'll need to refer to the datasheet for that one probably.

It is definitely not textbook input handling and if that freaks you out this probably isn't the thread for you.
If you're looking for someone who needs an explanation of basic input polling and debouncing routines I'm not your guy.

Again - thanks for making an effort to help

matmanj:
It is definitely not textbook input handling and if that freaks you out this probably isn't the thread for you.
If you're looking for someone who needs an explanation of basic input polling and debouncing routines I'm not your guy.

Why so angry? I also feel that you didn't get some details right :frowning:

Let's have a look at this code:

aarg:
Simplified test code (untested):

bool switchStatus()

{
static bool state;
if (pinR == LOW) state = false;
if (pinS == LOW) state = true;
return state;
}

Another great code snippet, worth to remember :slight_smile:

It has all requested properties, i.e. reacts immediately on changes, and doesn't produce artefacts from bouncing switches. With "immediately" meaning exactly at the time when the button state is examined. There is no need or use of a state indicator, which is set exactly when the switch toggles, when the code is not ready to act upon an eventual change at that time. If one needs really faster reaction on changes, interrupts are the only way for an immediate reaction, independent from what the main thread code actually does.

Instead ordinary debounce code (for one signal line) adds a delay, that is not necessary at all. As soon as the input becomes low, it's definitely known that the switch is in "make" state, the delay is required only to prevent false interpretation of subsequent bouncing spikes. No problem with mechanical switches (push buttons...), which can not be pressed and released faster than the assumed debounce delay. Similar for "break", here also the first occurrence of a high input (break), after the low debounce delay, will indicate a switch state change. A faster implementation, WRT reaction time, the debounce code should change the detected state at the begin of the debounce interval, not after that delay elapsed.

About the behaviour of flip flops: an RS has complementary outputs which are not exactly complementary. With both inputs (of a NAND RS) being low, both outputs will be high at the same time.

aarg:

volatile bool state;

bool switchStatus()
{
  bool st;
  nointerrupts();
  st = state;
  interrupts();
  return st;
}

void pinR_ISR
{
state = false;
}

void pinS_ISR
{
state = true;
}

Reading a byte size variable is an atomic (indivisible) operation, no need to disable interrupts for reading such flags.
"Volatile" may be required, to prevent the compiler from caching the initial state of the pin, in a lengthy subroutine that definitely can (and should) handle state changes during an invocation.

And a note on "bool": that type is slow and produces more instructions than for handling "byte" type variables.
When the C language forces the result of boolean expressions (comparisons...) into either 0 or 1, there is no use for the excess code related to bool variables. Perhaps the compiler does a better job in more aggressive optimization levels?

I have grossly simplified the original code to help make it easier to understand the code I actually wanted to highlight. The comments from the original code listing would probably help. It's only a few lines of code but it's doing everything a basic switch input routine should and it's only doing it if a pin change is sensed by the interrupt hardware.

void setup() {
  pinMode(13, OUTPUT); 
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  PCMSK0 = 0x10;
}

void loop() {
  if (PCIFR) { 
    byte switchState = (PCMSK0 ^= 0x30) >> 5; 
    PCIFR = 0x01;
    digitalWrite(13, switchState);
  }
}

P.S. I'm not angry. Just trying to clarify the intent of my original post.

aarg:

void pinR_ISR

{
state = false;
}

void pinS_ISR
{
state = true;
}

That is a basic subset of what my code does.

While it's functional one problem is that the ISR will be called again every time the switch bounces resulting in many wasted cycles. My code disables sensing on that pin after the first break and only enables it again when the opposite side breaks.

Secondly, if the only thing the ISR is used for is to set a flag in a variable it is wasting cycles and space. PCIFR already contains the flag we need.

Correct me if I'm wrong but by fixing the problems with the code designed to be better than my code we have arrive at - my code?

P.S. OK maybe a tiny bit angry now. I should sleep.

While it's functional one problem is that the ISR will be called again every time the switch bounces resulting in many wasted cycles. My code disables sensing on that pin after the first break and only enables it again when the opposite side breaks.

Your original post caught my interest because this is where saw a potential advantage ... adding multiple interrupts that are unaffected by noise or bounce. So couldn't the pin change enable mask register PCMSK0 be utilized in such a way that the "Set" pin interrupt is immediately turned off until the "Reset" pin interrupt is detected and vice-versa. This could automatically eliminate undesired "bounce" interrupts.

EDIT: Oh, I see you've already covered that in some other way ... I'll need to delve into this one day.

aarg:
There is absolutely no reason to use millis() or any other timing here. That's the beauty of it. :slight_smile:

I didn't know millis() was in any way ugly.

Why would one avoid using millis() if it gets the job done simply?

...R

Robin2:
I didn't know millis() was in any way ugly.

Why would one avoid using millis() if it gets the job done simply?

...R

It's beautiful when it's required. But in this case, it's just not needed. RS inputs completely eliminate any need for timing.

matmanj:
Some good points thank you guys.

Here's a much simpler demonstration of the concept:

/*  unbounce 03 

*  fast polling an SPDT switch using INT flags
*  ** not using interrupts, just the flags
*  for arduino micro (atmega32U4)

*  switch connections:               
*                NO -> Arduino pin 2 (PD1 - INT1)
*          GND--
*                NC -> Arduino pin 3 (PD0 - INT0)
*/

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  EICRA |= 0x0A;
}

void loop() {
  if (EIFR & 0x03) {
    byte switchStatus = EIFR & 0x02;
    EICRA = EICRA & 0xF0 | 0x02 << switchStatus;
    // do stuff that happens in response to the switch 
  }
  // do loop stuff here regardless of switch state
}




I think a similar setup could be applied to an SPST switch with debouncing done lockout style using a similar routine with a timer interrupt.

The main advantages I'm looking at here are less loop overheads and no delays caused if the contact happens to be bouncing when it's polled.

Thanks, that is a lot easier to understand. If I do understand correctly, you are leveraging the fact that a low on one of the inputs is latched (i.e. remembered). If the input goes high again, the flag will remain low until cleared (read). Thus you believe that you have stabilized the switch bounce pulse train. It is true, you have.

But it is useless information that you have captured. Because, with the RS configuration, the detection of a low at any time on either input is ipso facto evidence not that switch bounce is terminated, but that it can be ignored.

It is important to consider the application context in which this is used. In a truly event driven software architecture, polling would be a bad thing because you would have to construct a periodic poll. In that case, a solution like I posted in reply #11 would be awkward. But you make reference above both to a loop and polling. That is actually how the vast majority of Arduino software operates. In such a case, polling (asking for information from a device only when you need it) is normal, acceptable and wise.

Thus, although your system works, it has no useful advantage. It has the disadvantage of using hardware features that might be required for other purposes. It is not simple. The theory that it is based on, attempts to solve a problem that really doesn't exist. I also doubt that it can be faster than applying a direct read of the R and S pin using port manipulation, to the solution that I posted. Probably slower.

A fair comparison of techniques would require an agreement to implement a real world wrapper function, restricted to a single switch, like at least:

bool switchState();

that returns the current state of the key, and possibly:

bool keyOn();
bool keyOff();

although it seems to me that those are trivial add-ons that can be derived from the first.

It's possible that your idea might have some useful application in situations where the switch events happen faster than the program can poll them. The classic case is tachometer sensor pulses. But that is a different problem, with a different application interface.

Remembering back a few decades-- i think AARG is corect.. rs FF is independent of any clocking..
JK FF required clocking..
i could be wrong..

Getting some helpful feedback here - thanks guys. It has helped me clarify to myself exactly what my code is doing. Let me step through what these 3 or 4 lines of code are doing. (single SPDT switch scenario)

  1. If a change hasn't been flagged by the interrupt hardware skip the rest of this code and perform this check on the next loop. This means handling polled inputs this way adds only one instruction to the main loop unless there is work to be done. If the pin change flag is set move on to step 2.

  2. Toggle the interrupt mask. Stop sensing on the contact that just changed and start sensing on the opposite contact of the switch. Why? Because this contact is going to keep bouncing for a while and we only want a stable signal - not noise. Conveniently the opposite contact of the switch is absolutely stable now.

  3. Reset the interrupt flag. Now step 1 will skip the rest of these steps until the switch actually next changes.

  4. For convenience generate a temporary variable for checking if the switch is on or off by copying the interrupt mask and transforming it into a tidy boolean. This step isn't required for this method to be complete. It's possibly faster to just directly read the interrupt mask through an & mask when the information is needed.

  5. Run code that should happen when the switch changes.

One complete operation of a Form-C contact or SPST switch having considerable noise or bounce, generating possibly hundreds of interrupts, with your code would only create 2 interrupts per complete operation (cycle) and produce a clean output without any introduced delays or debounce code required. (whew!).

As would my 5 lines of code. But creating no interrupts.

As would my 5 lines of code. But creating no interrupts.

Looks like we now have the best of each method ... polling and time critical applications.

Time to address some valid disadvantages that have been raised.

  1. Makes use of a limited hardware resource. The impact of this is something that can only be determined with full knowledge of the project at hand. I reject the notion that utilizing limited hardware resources should always be avoided.

  2. Not portable. It's true that my code is targeted at the ATmega32U4 (Leonardo/Micro). While I haven't done so myself I think it would be possible to make a library that applies this method across all Arduino platforms.

  3. The performance difference is minimal. For my stated purpose (USB keyboard) I would have to agree. Even so, all else being equal, I choose to use the method that is faster and more frugal. There are definitely applications out there that would benefit from shaving clocks off of the main loop or having one less static variable or having a switch that is reliable in the single microsecond range.

  4. The code is ugly and difficult to understand. ...but I'm improving it. See post #23 for my most recent attempt at streamlining the code. As far as I know attachInterrupt() doesn't allow for configuring interrupt registers without actually enabling the interrupt or I would be using it.

And a disadvantage that I would like to refute...

  • It's no better than polling the switch with a textbook RS flip flop in software.

It is certainly better but maybe insignificantly so.

Firstly the method itself executes faster and uses less program memory and less data memory.

Secondly when we poll the input immediately after a contact "make", remembering that we can't know yet whether the make happened and that's why we're polling it every loop:

The first time we poll the switch after "make" it may or may not have closed contacts due to bouncing. If it has bounced open at the time of the poll our code will take the "nothing happened" branch just like every loop before the break. Since bounce is somewhat random this has a receding chance of happening through several loop cycles.

With my code we're still polling but polling the interrupt flag, which is configured to be set to true asynchronously by any contact make event. So the first time I poll the interrupt after the make my code always takes the "something happened" branch.

Calling this method a "software RS latch" is approximately as accurate as calling the other methods shown in this thread "software RS flip-flop".

How much better it is than pin polling is a function of loop interval and acceptable input response time.

I need to clarify a couple of things.

I am NOT enabling any interrupts and the PCINTO hardware is not interrupting the program flow at all.

I AM polling the status of the switch with each pass of loop(). I'm just doing it better with the help of the interrupt hardware.

  1. Reset the interrupt flag.

In general, that creates a race condition. That is an advantage to allowing the interrupt to occur: the hardware eliminates the race condition.

I am NOT enabling any interrupts and the PCINTO hardware is not interrupting the program flow at all.

Easily adapted to enable interrupts, but use an RS style enable/disable to eliminate unwanted interrupts. Would be great for pulse width, timing and frequency applications.

  1. Toggle the interrupt mask < removes the possibility of a race condition

I would say this is the pivotal trick of this method.

dlloyd:
Easily adapted to enable interrupts.

I think you might find many real time applications do as well or better if you leave interrupts disabled.

My little led toggle example from earlier polls the switch at probably 4MHz (complete guess). If a project required tight predictable response and could do without all interrupts I would actually consider disabling interrupts in setup(). The result would be a completely regular polling interval except when branching to handle an event. If you're polling interrupt flags you won't even miss the first input event that happens while you're handling the previous event.

Even if you do use interrupts only one subsequent event can be queued while you're handling the current ISR.

matmanj:
2. Toggle the interrupt mask < removes the possibility of a race condition

Does it? What would you like to wager?