clock pulse generator for a PLC - clock

I am working with PLCs trying to design a water tank. On one section of the design I am asked to create a clock pulse generator. I am currently trying to do this using ladder diagrams.
I believe I have the logic correct just cant seem to put it together. I want a counter to count the clock pulses that I generate, then I store these pulese in a data memory to ensure the count is retained if the system is switched off and on.
question is how do I design this clock pulse generator.
Kind regards

There are a few different ways to create a pulse generator (or commonly known in the plc world as a BLINK timer). As a matter of fact many plc programming softwares have this function block built in to their function block libraries. But if they don't or you just want to make your own you can do something like this
VAR
ton1: TON;
ton2: TON;
StartPulse: BOOL;
startPulseTrig: R_TRIG;
LatchPulseInitial: BOOL;
PulseOutput: BOOL;
Timer1Done: BOOL;
Timer2Done: BOOL;
PulseWidth:TIME:=t#500ms;
END_VAR
If you would like to count the number of pulses and store this value to a variable you can use a simple CTU (counter up) block available in all plc languages.
Review of functionality
The StartPulse variable can be anything you want that will start the counter. In my case I just used an internal bool variable that I turned on. If you want this timer to start automatically when the plc starts then just initialize this variable to true. Because StartPulse only works on the rising edge of the signal the LatchPulseInitial coil will only ever be set once.
When the LatchPulseInitial variable goes true this will start ton1 a Timer On Delay (TON) function block. This will delay the output of the block from turning on for the time of PT (in my case I have 500ms).
After 500ms has expired the ton1 outputs will turn on. this will turn on the Timer1Done variable and turn off the Timer2Done and LatchPulseInitial. Now that LatchPulseInitial has been turned off it will never interfere with the program again since it can only be turned on by the rising edge of StartPulse. Note: once the block has reached PT the outputs will stay on until the input to the block is removed.
Since Timer1Done is now on ton2 will start counting until PT is reached. Once PT is reached the outputs for that block will turn on. This will reset Timer1Done and set Timer2Done This will start ton1 again and thus starting the whole process over.
For the PulseOutput, which is the actual pulse output you are looking for, I have this being set to true when Timer2Done is true. This is because when this variable is true it is the high state of the pulse generator. When Timer1Done is true it is the low state of the pulse generator.
When the PulseOutput goes true it will trigger the input on the CTU which will increment the count of the variable in CV (current value) by 1.
If you are going to be using this logic in numerous places in your program or you plan on reusing it in the future I would make this into its own function block so you won't have to repeat this logic everytime you want to make this type of timer.

Once I had to create a BLINK FB. It is written in Structured Text. But it is suitable to use in a ladder logic program and IN/OUT Variables are named like TON style. The Blink starts with Q = TRUE. If you want to start with FALSE just invert Q and switch the Times!
FUNCTION_BLOCK BLINK
VAR_INPUT
IN : BOOL;
PT_ON : TIME;
PT_OFF : TIME;
END_VAR
VAR_OUTPUT
Q : BOOL;
ET : TIME;
END_VAR
VAR
rtIN : R_TRIG;
tonBlink : TON;
END_VAR
rtIN(CLK := IN);
IF tonBlink.Q OR rtIN.Q THEN
(*Toggle Output*)
Q := NOT Q;
(*Timer Reset call, important to call timer twice in same cycle for correct Blink Time*)
tonBlink(IN:= FALSE);
(*Set corresponding Time*)
IF Q THEN
tonBlink.PT := PT_ON;
ELSE
tonBlink.PT := PT_OFF;
END_IF
END_IF
(*Timer Run call*)
tonBlink(IN:= IN);
IF IN THEN
ET := tonBlink.ET;
ELSE
ET := T#0S;
Q := FALSE;
END_IF

In my opinion, this is the most straightforward way to do it, using 1 timer, up counter and modulo operator:
Blink function in ladder
Also note, if your PLC doesnt have modulo, then multiply by -1 each time.

Related

Difference in timing while getting values from ROM or RAM

So I am having a hard time understanding the time it takes to get values from ram or rom in vhdl. To insert data into ram I know happens on the rising edge of the clock and takes one clock such as the example I have, but in terms of getting data out does it take one clock to get the data from memory and then get then another clock cycle to get the data to output meaning it takes 2 clock cycles to get data?
process(clk)
begin
if(rising_edge(clk)) then
if(write_en = '1') then
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
end if;
end if;
end process;
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
No, it takes 1 clock cycle:
In your code you have two concurrent processes. One is explicit:
process(clk)
begin
if(rising_edge(clk)) then
if(write_en = '1') then
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
end if;
end if;
end process;
The other is implicit; it is a concurrent signal assignment:
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
The concurrent signal assignment is exactly equivalent to this:
process(address, mem)
begin
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
end process;
In other words, it is equivalent to a process with address and mem in the sensitivity list. Any concurrent signal assignment is equivalent to a process with all the inputs in the sensitivity list. An input to a concurrent signal assignment is any signal on the right hand side of the signal assignment operator (<=). So, you get a sensitivity list for free and that is an advantage of using concurrent signal assignments: you cannot accidentally miss out a signal from the sensitivity list, because the compiler creates it for you.
So, lets consider what happens when each process is executed. The first process has just the signal clk in its sensitivity list, so the process executes whenever there is a change (an event) on clk. If this change is not a rising edge then the rising_edge function returns FALSE and the process immediately suspends. If this change is a rising edge then the rising_edge function returns TRUE and if the expression write_en = '1' is also TRUE then this line gets executed:
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
The effect of this line is to put an event on the event queue to drive the correct value of mem on the next delta cycle (assuming there is some change to the signal mem as a result). The event queue is the simulator's "to do" list; a delta cycle is one iteration of the simulator; the next iteration will occur once all the processes that are executing in the current iteration suspend.
So, the next iteration cycle occurs and the signal mem gets its new value. The signal mem is in the implicit sensitivity list of the second (implicit) process (the concurrent signal assignment). So, this second process starts executing and the line with the signal assignment to out_data is executed and (as with the executing of any line containing a signal assignment) an event is put on the event queue to drive the target signal - out_data in this case - to a new value (again assuming the value should change).
So, the change to the signal out_data always occurs one delta cycle after a change on the signal mem. We've already established that the signal mem changes one delta cycle after any rising edge on the signal clk, so we can see that the signal out_data changes two delta cycles after any rising edge on the signal clk.
Whilst it is vital to be aware of delta cycles when writing VHDL, we don't usually need to worry about them if we adopt a good, conventional style. So, we can just say that the signal out_data changes on any rising edge of the signal clk or, in other words, there is a delay of one clock cycle between any changes on the signals write_en, incoming_data or address and any corresponding change on the signal out_data.

PLC Object Oriented Programming - Using methods

I'm writing a program for a Schneider PLC using structured text, and I'm trying to do it using object oriented programming.
Being a newbie in PLC programming, I wrote a simple test program such a this:
okFlag:=myObject.aMethod();
IF okFlag THEN
// it's ok, go on
ELSE
// error handling
END_IF
aMethod must perform some operations, wait for the result (there is a "time-out" check to avoid deadlocks) and return TRUE or FALSE
This is what I expected during program execution
1) when the okFlag:=myObject.aMethod(); is reached, the code inside aMethod is executed until a result is returned. When I say "executed" I mean that in the next scan cycle the execution of aMethodcontinues from the point it had reached before.
2) the result of method calling is checked and the main flow of the program is executed
and this is what happens:
1) aMethod is executed but the program flow continues. That is, when it reaches the end of aMethod a value it's returned, even if the events that aMethod should wait for are still executing.
2) on the next cycle, aMethod is called again and restarts from the beginning
This is the first solution I found:
VAR_STATIC
imBusy: BOOL
END_VAR
METHOD aMethod: INT;
IF NOT(imBusy) THEN
imBusy:=FALSE;
aMethod:=-1; // result of method while in progress
ELSE
aMethod:=-1;
<rest of code. If everything is ok, the result is 0, otherwise is 1>
END_IF
imBusy:=aMethod<0;
and the main program:
CASE (myObject.aMethod()) OF
0: // it's ok, go on
1: // error handling
ELSE
// still executing...
END_CASE
and this seems to work, but I don't know if it's the right approach.
There are some libraries from Schneider which use methods that return boolean and seem to work as I expected in my program. That is: when the cycle reaches the call to method for the first time the program flow is "deviated" somehow so that in the next cycle it enters again the method until it's finished. It's there a way to have this behaviour ?
generally OOP isn't the approach that people would take when using IEC61131 languages. Your best bet is probably to implement your code as a state machine. I've used this approach in the past as a way of simplifying a complex sequence so that it is easier for plant maintainers to interpret.
Typically what I would recommend if you are going to take this approach is to try to segregate your state machine itself from your working code; you can implement a state machine of X steps, and then have your working code reference the statemachine step.
A simple example might look like:
stepNo := 0;
IF (start AND stepNo = 0) THEN
StepNo = 1;
END_IF;
(* there's a shortcut unity operation for resetting this array to zeroes which is faster, but I can't remember it off the top of my head... *)
ActiveStepArray := BlankStepArray;
IF stepNo > 0 THEN
IF StepComplete[stepNo] THEN
stepNo := stepNo +1;
END_IF;
ActiveStepArray[stepNo] := true;
END_IF;
Then in other code sections you can put...
IF ActiveStep[1] THEN
(* Do something *)
StepComplete[1] := true;
END_IF;
IF ActiveStep[2] THEN
(* Do Something *)
StepComplete[2] := true;
END_IF;
(* etc *)
The nice thing about this approach is that you can actually put all of the state machine code (including jumps, resets etc) into a DFB, test it and then shelve it, and then just use the active step, step complete, and any other inputs you require.
Your code is still always going to execute an entire section of logic, but if you really want to avoid that then you'll have to use a lot of IF statements, which will impede readability.
Hope that helps.
Why not use SFC it makes your live easier in many cases, since it is state machine language itself. Do subprogram, wait condition do another .. rince and repeat. :)
Don't hang just for ST, the other IEC languages are better in some other tasks and keep thing as clear as possible. There should be not so much "this is my cake" mentality on the industrial PLC programming circles as it is on the many other programming fields, since application timeline can be 40 years and you left the firm 20 years ago to better job and programs are almost always location/customer or atleast hardware specific.
http://www.automation.com/pdf_articles/IEC_Programming_Thayer_L.pdf

signal vs variable

VHDL provides two major object types to hold data, namel signal and variable, but I can't find anywhere that is clear on when to use one data-type over the other. Can anyone shed some light on their strengths/limitations/scope/synthesis/situations in which using one would be better than the other?
Signals can be used to communicate values between processes. Variables cannot. There are shared variables which can in older compilers, but you really are asking for problems (with race conditions) if you do that - unless you use protected types which are a bit like classes. Then they are same to use for communication, but not (as far as I know) synthesisable.
This fundamental restriction on communication comes from the way updates on signals and variables work.
The big distinction comes because variables update immediately they are assigned to (with the := operator). Signals have an update scheduled when assigned to (with the <= operator) but the value that anyone sees when they read the signal will not change until some time passes.
(Aside: That amount of time could be as small as a delta cycle, which is the smallest amount of time in a VHDL simuator - no "real" time passes. Something like wait for 0 ps; causes the simulator to wait for the next delta cycle before continuing.)
If you need the same logic to feed into multiple flipflops a variable is a good way of factoring that logic into a single point, rather than copying/pasting code.
In terms of logic, within a clocked process, signals always infer a flipflop. Variables can be used for both combinatorial logic and inferring a flipflop. Sometimes both for the same variable. Some think this confusing, personally, I think it's fine:
process (clk)
variable something : std_logic;
if rising_edge(clk) then
if reset = '1' then
something := '0';
else
output_b <= something or input c; -- using the previous clock's value of 'something' infers a register
something := input_a and input_b; -- comb. logic for a new value
output_a <= something or input_c; -- which is used immediately, not registered here
end if;
end if;
end process;
One thing to watch using variables is that because if they are read after they are written, no register output is used, you can get long chains of logic which can lead to missing your fmax target
One thing to watch using signals (in clocked processes) is that they always infer a register, and hence leads to latency.
As others have said signals get updated with their new value at the end of the time slice, but variables are updated immediately.
// inside some process
// varA = sigA = 0. sigB = 2
varA := sigB + 1; // varA is now 3
sigC <= varA + 1; // sigC will be 4
sigA <= sigB + 1; // sigA will be 3
sigD <= sigA + 1; // sigD will be 1 (original sigA + 1)
For hardware design, I use variables very infrequently. It's normally when I'm hacking in some feature that really needs the code to be re-factored, but I'm on a deadline. I avoid them because I find the mental model of working with signals and variables too different to live nicely in one piece of code. That's not to say it can't be done, but I think most RTL engineers avoid mixing... and you can't avoid signals.
Other points:
Signals have entity scoping. Variables are local to the process.
Both synthesize

Keeping time using timer interrupts an embedded microcontroller

This question is about programming small microcontrollers without an OS. In particular, I'm interested in PICs at the moment, but the question is general.
I've seen several times the following pattern for keeping time:
Timer interrupt code (say the timer fires every second):
...
if (sec_counter > 0)
sec_counter--;
...
Mainline code (non-interrupt):
sec_counter = 500; // 500 seconds
while (sec_counter)
{
// .. do stuff
}
The mainline code may repeat, set the counter to various values (not just seconds) and so on.
It seems to me there's a race condition here when the assignment to sec_counter in the mainline code isn't atomic. For example, in PIC18 the assignment is translated to 4 ASM statements (loading each byte at the time and selecting the right byte from the memory bank before that). If the interrupt code comes in the middle of this, the final value may be corrupted.
Curiously, if the value assigned is less than 256, the assignment is atomic, so there's no problem.
Am I right about this problem?
What patterns do you use to implement such behavior correctly? I see several options:
Disable interrupts before each assignment to sec_counter and enable after - this isn't pretty
Don't use an interrupt, but a separate timer which is started and then polled. This is clean, but uses up a whole timer (in the previous case the 1-sec firing timer can be used for other purposes as well).
Any other ideas?
The PIC architecture is as atomic as it gets. It ensures that all read-modify-write operations to a memory file are 'atomic'. Although it takes 4-clocks to perform the entire read-modify-write, all 4-clocks are consumed in a single instruction and the next instruction uses the next 4-clock cycle. It is the way that the pipeline works. In 8-clocks, two instructions are in the pipeline.
If the value is larger than 8-bit, it becomes an issue as the PIC is an 8-bit machine and larger operands are handled in multiple instructions. That will introduce atomic issues.
You definitely need to disable the interrupt before setting the counter. Ugly as it may be, it is necessary. It is a good practice to ALWAYS disable the interrupt before configuring hardware registers or software variables affecting the ISR method. If you are writing in C, you should consider all operations as non-atomic. If you find that you have to look at the generated assembly too many times, then it may be better to abandon C and program in assembly. In my experience, this is rarely the case.
Regarding the issue discussed, this is what I suggest:
ISR:
if (countDownFlag)
{
sec_counter--;
}
and setting the counter:
// make sure the countdown isn't running
sec_counter = 500;
countDownFlag = true;
...
// Countdown finished
countDownFlag = false;
You need an extra variable and is better to wrap everything in a function:
void startCountDown(int startValue)
{
sec_counter = 500;
countDownFlag = true;
}
This way you abstract the starting method (and hide ugliness if needed). For example you can easily change it to start a hardware timer without affecting the callers of the method.
Write the value then check that it is the value required would seem to be the simplest alternative.
do {
sec_counter = value;
} while (sec_counter != value);
BTW you should make the variable volatile if using C.
If you need to read the value then you can read it twice.
do {
value = sec_counter;
} while (value != sec_counter);
Because accesses to the sec_counter variable are not atomic, there's really no way to avoid disabling interrupts before accessing this variable in your mainline code and restoring interrupt state after the access if you want deterministic behavior. This would probably be a better choice than dedicating a HW timer for this task (unless you have a surplus of timers, in which case you might as well use one).
If you download Microchip's free TCP/IP Stack there are routines in there that use a timer overflow to keep track of elapsed time. Specifically "tick.c" and "tick.h". Just copy those files over to your project.
Inside those files you can see how they do it.
It's not so curious about the less than 256 moves being atomic - moving an 8 bit value is one opcode so that's as atomic as you get.
The best solution on such a microcontroller as the PIC is to disable interrupts before you change the timer value. You can even check the value of the interrupt flag when you change the variable in the main loop and handle it if you want. Make it a function that changes the value of the variable and you could even call it from the ISR as well.
Well, what does the comparison assembly code look like?
Taken to account that it counts downwards, and that it's just a zero compare, it should be safe if it first checks the MSB, then the LSB. There could be corruption, but it doesn't really matter if it comes in the middle between 0x100 and 0xff and the corrupted compare value is 0x1ff.
The way you are using your timer now, it won't count whole seconds anyway, because you might change it in the middle of a cycle.
So, if you don't care about it. The best way, in my opinion, would be to read the value, and then just compare the difference. It takes a couple of OPs more, but has no multi-threading problems.(Since the timer has priority)
If you are more strict about the time value, I would automatically disable the timer once it counts down to 0, and clear the internal counter of the timer and activate once you need it.
Move the code portion that would be on the main() to a proper function, and have it conditionally called by the ISR.
Also, to avoid any sort of delaying or missing ticks, choose this timer ISR to be a high-prio interrupt (the PIC18 has two levels).
One approach is to have an interrupt keep a byte variable, and have something else which gets called at least once every 256 times the counter is hit; do something like:
// ub==unsigned char; ui==unsigned int; ul==unsigned long
ub now_ctr; // This one is hit by the interrupt
ub prev_ctr;
ul big_ctr;
void poll_counter(void)
{
ub delta_ctr;
delta_ctr = (ub)(now_ctr-prev_ctr);
big_ctr += delta_ctr;
prev_ctr += delta_ctr;
}
A slight variation, if you don't mind forcing the interrupt's counter to stay in sync with the LSB of your big counter:
ul big_ctr;
void poll_counter(void)
{
big_ctr += (ub)(now_ctr - big_ctr);
}
No one addressed the issue of reading multibyte hardware registers (for example a timer.
The timer could roll over and increment its second byte while you're reading it.
Say it's 0x0001ffff and you read it. You might get 0x0010ffff, or 0x00010000.
The 16 bit peripheral register is volatile to your code.
For any volatile "variables", I use the double read technique.
do {
t = timer;
} while (t != timer);

Simple Debounce Routine

Do you have a simple debounce routine handy to deal with a single switch input?
This is a simple bare metal system without any OS.
I would like to avoid a looping construct with a specific count, as the processor speed might fluctuate.
I think you could learn a lot about this here: http://www.ganssle.com/debouncing.pdf
Your best bet is always to do this in hardware if possible, but there are some thoughts on software in there as well.
Simple example code from TFA:
#define CHECK_MSEC 5 // Read hardware every 5 msec
#define PRESS_MSEC 10 // Stable time before registering pressed
#define RELEASE_MSEC 100 // Stable time before registering released
// This function reads the key state from the hardware.
extern bool_t RawKeyPressed();
// This holds the debounced state of the key.
bool_t DebouncedKeyPress = false;
// Service routine called every CHECK_MSEC to
// debounce both edges
void DebounceSwitch1(bool_t *Key_changed, bool_t *Key_pressed)
{
static uint8_t Count = RELEASE_MSEC / CHECK_MSEC;
bool_t RawState;
*Key_changed = false;
*Key_pressed = DebouncedKeyPress;
RawState = RawKeyPressed();
if (RawState == DebouncedKeyPress) {
// Set the timer which allows a change from current state.
if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC;
else Count = PRESS_MSEC / CHECK_MSEC;
} else {
// Key has changed - wait for new state to become stable.
if (--Count == 0) {
// Timer expired - accept the change.
DebouncedKeyPress = RawState;
*Key_changed=true;
*Key_pressed=DebouncedKeyPress;
// And reset the timer.
if (DebouncedKeyPress) Count = RELEASE_MSEC / CHECK_MSEC;
else Count = PRESS_MSEC / CHECK_MSEC;
}
}
}
Simplest solutions are often the best, and I've found that simply only reading the switch state every N millseconds (between 10 and 50, depending on switches) has always worked for me.
I've stripped out broken and complex debounce routines and replaced them with a simple slow poll, and the results have always been good enough that way.
To implement it, you'll need a simple periodic timer interrupt on your system (assuming no RTOS support), but if you're used to programming it at the bare metal, that shouldn't be difficult to arrange.
Note that this simple approach adds a delay to detection of the change in state. If a switch takes T ms to reach a new steady state, and it's polled every X ms, then the worst case delay for detecting the press is T+X ms. Your polling interval X must be larger than the worst-case bounce time T.
There's no single simple solution that works for all types of buttons. No matter what someone here tells you to use, you'll have to try it with your hardware, and see how well it works. And look at the signals on a scope, to make sure you really know what's going on. Rich B's link to the pdf looks like a good place to start.
I have used a majority vote method to debounce an input. I set up a simple three state shift register type of data structure, and shift each sample and take the best two out of three as the "correct" value. This is obviously a function of either your interrupt handler, or a poller, depending on what method is used to actually read the hardware.
But, the best advice is to ask your friendly hardware designer to "latch" the value and allow you to clear this value when you get to it.
To debounce, you want to ignore any switch up that lasts under a certain threshold. You can set a hardware timer on switch up, or use a flag set via periodic interrupt.
If you can get away with it, the best solution in hardware is to have the switch have two distinct states with no state between. That is, use a SPDT switch, with each pole feeding either the R or S lines of a flip/flop. Wired that way, the output of the flip/flop should be debounced.
The algorithm from ganssle.com could have a bug in it. I have the impression the following line
static uint8_t Count = RELEASE_MSEC / CHECK_MSEC;
should read
static uint8_t Count = PRESS_MSEC / CHECK_MSEC;
in order to debounce correctly the initial press.
At the hardware level the basic debouncing routine has to take into account the following segments of a physical key's (or switch's) behavior:
Key sitting quietly->finger touches key and begins pushing down->key reaches bottom of travel and finger holds it there->finger begins releasing key and spring pushes key back up->finger releases key and key vibrates a bit until it quiesces
All of these stages involve 2 pieces of metal scraping and rubbing and bumping against each other, jiggling the voltage up and down from 0 to maximum over periods of milliseconds, so there is electrical noise every step of the way:
(1) Noise while the key is not being touched, caused by environmental issues like humidity, vibration, temperature changes, etc. causing voltage changes in the key contacts
(2) Noise caused as the key is being pressed down
(3) Noise as the key is being held down
(4) Noise as the key is being released
(5) Noise as the key vibrates after being released
Here's the algorithm by which we basically guess that the key is being pressed by a person:
read the state of the key, which can be "might be pressed", "definitely is pressed", "definitely is not pressed", "might not be pressed" (we're never really sure)
loop while key "might be" pressed (if dealing with hardware, this is a voltage sample greater than some threshold value), until is is "definitely not" pressed (lower than the threshold voltage)
(this is initialization, waiting for noise to quiesce, definition of "might be" and "definitely not" is dependent on specific application)
loop while key is "definitely not" pressed, until key "might be" pressed
when key "might be" pressed, begin looping and sampling the state of the key, and keep track of how long the key "might be" pressed
- if the key goes back to "might not be" or "definitely is not" pressed state before a certain amount of time, restart the procedure
- at a certain time (number of milliseconds) that you have chosen (usually through experimenting with different values) you decide that the sample value is no longer caused by noise, but is very likely caused by the key actually being held down by a human finger and you return the value "pressed"
while(keyvalue = maybepressed){
//loop - wait for transition to notpressed
sample keyvalue here;
maybe require it to be "notpressed" a number of times before you assume
it's really notpressed;
}
while(keyvalue = notpressed){
//loop - wait for transition to maybepressed
sample keyvalue
again, maybe require a "maybepressed" value a number of times before you
transition
}
while(keyvalue=maybepressed){
presstime+=1;
if presstime>required_presstime return pressed_affirmative
}
}
return pressed_negative
What I usually do is have three or so variables the width of the input register. Every poll, usually from an interrupt, shift the values up one to make way for the new sample. Then I have a debounced variable formed by setting the logical-and of the samples, and clearing the inverse logical-or. i.e. (untested, from memory)
input3 = input2;
input2 = input1;
input1 = (*PORTA);
debounced |= input1 & input2 & input3;
debounced &= (input1 | input2 | input3);
Here's an example:
debounced has xxxx (where 'x' is "whatever")
input1 = 0110,
input2 = 1100,
input3 = 0100
With the information above,
We need to switch only bit 2 to 1, and bit 0 to 0. The rest are still "bouncing".
debounced |= (0100); //set only bit 2
debounced &= (1110); //clear only bit 0
The result is that now debounced = x1x0
use integration and you'll be a happy camper. Works well for all switches.
just increment a counter when read as high and decrement it when read as low and when the integrator reaches a limit (upper or lower) call the state (high or low).
The whole concept is described well by Jack Ganssle. His solution posted as an answer to the original question is very good, but I find part of it not so clear how does it work.
There are three main ways how to deal with switch bouncing:
- using polling
- using interrupts
- combination of interrupts and pooling.
As I deal mostly with embedded systems that are low-power or tend to be low-power so the answer from Keith to integrate is very reasonable to me.
If you work with SPST push button type switch with one mechanically stable position then I would prefer the solution which works using a combination of interrupt and pooling.
Like this: use GPIO input interrupt to detect first edge (falling or rising, the opposite direction of un-actuated switch state). Under GPIO input ISR set flag about detection.
Use another interrupt for measuring time (ie. general purpose timer or SysTick) to count milliseconds.
On every SysTick increment (1 ms):
IF buttonFlag is true then call function to poll the state of push button (polling).
Do this for N consecutive SysTick increments then clear the flag.
When you poll the button state use logic as you wish to decide button state like M consecutive readings same, average more than Z, count if the state, last X readings the same, etc.
I think this approach should benefit from responsiveness on interrupt and lower power usage as there will be no button polling after N SysTick increments. There are no complicated interrupt modifications between various interrupts so the program code should be fairly simple and readable.
Take into consideration things like: do you need to "release" button, do you need to detect long press and do you need action on button release. I don't like button action on button release, but some solutions work that way.