https://www.mikrocontroller.net/articles/Entprellung
|
Welcome to the Mikrocontroller.net article collection. All articles here can be edited by everyone according to the wiki principle. To the main page of the article collection
DevelopmentFrom the Mikrocontroller.net article collection, with contributions from various authors (see version history)
Table of Contents[ Hide ] Problem DescriptionMechanical live components such as switches and buttons tend to bounce when switched on and off , ie they quickly switch off and on several times, which is caused by mechanical vibrations of the switch contact, unless they are protected against it with complex mechanical measures. The encoders in particular are sensitive to this due to the detent positions and the movement of the operator. Optoelectronic components and chemical contact switches as well as liquid switches also have the problem. Simply put, a voltage switched by a bouncing switch or button looks like this: There are therefore several short switch-on pulses, which can be interpreted as multiple commands for push buttons and as wrong angular movements for rotary encoders. With switches, on the other hand, there are several resets and switch-on processes in the electronic assembly, which draw power unnecessarily or, in the worst case, stress or damage the circuit. Important switches and those that are supposed to carry high currents are protected by suitable measures such as redundancy, tap changer concepts or, in the case of gas and liquid switches, by electrochemical measures. This is saved with simple switches. Since there is no safe way to avoid these effects with these simple, unprotected switches, the wrong signal must be evaluated sensibly by the electronics. There are different approaches for this: HardwareentprellungBounce-free switchesAs already indicated, the electromechanical industry has various special constructions for special applications that generate clean switching states to the outside by using either mechanical damping in the form of a self-locking spring mechanism or an integrated electronic signal delay. However, such systems are expensive and are mostly used only in the performance range. In addition, they are not 100% safe and fail due to aging. Wherever possible, further measures are therefore taken to suppress bouncing. Changeover switchA classic RS flip-flop can be used to debounce toggle switches . In this variant, in addition to two NAND gates, only two pull-up resistors are required. In the switch position shown, level 0 is present at position / S. This sets the flip-flop and the output at level 1. If the switch between contacts 2 and 3 closes, level 0 is present at position / R. This means that the output of the flip-flop goes to level 0. As soon as the switch changes from one contact to the other, it usually begins to bounce. During the bouncing, the switch switches between the two states “switch touches contact” and “switch is free in the air”. The output of the flip-flop remains stable during this bounce time, since the switch never touches the opposite contact during the bounce and the RS flip-flop can keep its state alone. The bounce time is strongly dependent on the switch type and is between 0.1 and 10ms. The dimensioning of the resistors is relatively uncritical. 100kOhm can be used as a guideline. Toggle switch without flip-flopIf you don’t have a flip-flop at hand, you can also use this circuit. How it works: When switching, the capacitor is always immediately recharged. While the contact bounces, it is in the air and has no connection. During this time, the capacitor maintains the level. Dimensioning: If the debounced button is connected to an IC, the input leakage current is the decisive current. If further currents flow, these must be taken into account. With a microcontroller from Atmel, 1µA is typical. The following applies:
Since a bruise lasts about 10ms and the voltage should drop by a maximum of 0.5V during this time, the following capacity is reached:
A resistor can be added to reduce current peaks. A time constant of 1µs to 1ms seems reasonable. So 500 Ohm to 500kOhm can be used, whereby the current peaks are higher at low resistance, and the pin current becomes disruptive at 500kOhm. Simple buttonEven if the RS flip-flop is very effective, this variant of the debouncing is rarely used. The reason for this is that simple switches are used more often in circuits. These are often smaller and cheaper. To debounce simple push buttons / momentary switch, a simple RC low pass can be used. A capacitor is charged or discharged via a resistor depending on the switch position. The RC element forms a low pass, so that the voltage across the capacitor cannot jump from one level to the other. When the switch is open, the capacitor slowly charges to V cc through the two resistors R 1 and R 2 . When the switchover threshold is reached, the output jumps to level 0. If the switch is closed, the capacitor slowly discharges through resistor R 2 . Accordingly, the output of the inverter changes to level 1. While the button is bouncing, the voltage across the capacitor cannot change abruptly, since the charging and discharging takes place rather slowly via the resistors. In addition, the switching thresholds for the transition LOW-> HIGH and HIGH-> LOW are very different (hysteresis, see article Schmitt trigger). If the components are dimensioned correctly, the output of the inverter is thus free of bounce. Please note that the inverter must be one with Schmitt trigger inputs, because the output is not defined for standard logic inputs in the range of typically 0.8V – 2.0V. For example, the 74HC14 or CD40106 (pin compatible) can be used as an inverter. Alternatively, a CD4093 can be used. The CD4093 is a NAND gate with Schmitt trigger inputs. To make an inverter out of a NAND gate, simply connect the two inputs or set one input to HIGH. For a suitable dimensioning you have to juggle something with the standard formulas for a capacitor. The voltage across the capacitor during discharge is calculated according to:
For the output of the inverter to be stable, the voltage across the capacitor and thus the voltage at the input of the inverter must remain above the voltage at which the inverter switches. This threshold voltage is exactly the time-dependent voltage across the capacitor.
By changing the formula:
A button usually bounces about 10ms. For safety reasons, a bounce time of 20ms can be assumed when calculating the resistance. U_0 is the operating voltage Vcc. The threshold voltage must be read from the data sheet of the Schmitt trigger used. The 74HC14 has a value of 2.0V. If you take 1µF for the capacitor and the operating voltage is 5V, the resistance is about 22kOhm. If the switch is opened, the capacitor charges according to the following formula:
With U_th = U_C, the changeover to (R_1 + R_2) results in:
A value of 2.3V can be read from the data sheet for the threshold voltage. With this value and the assumptions from above there is a value of 32kOhm for R_1 + R_2. This results in a value of approximately 10 kOhm for R_1. Note: For Hitachi 74LS14 e.g. B. the upper and lower switching threshold values are different. Care must be taken that U_ {th} takes the lower threshold when unloading and U_ {th} takes the upper threshold when loading. SoftwareentprellungIn times of electronic evaluation of buttons and switches, debouncing software is often cheaper than using an expensive switch. For this reason, computer keyboards, for example, are no longer equipped with low-bounce keys or debounce capacitors. If you use the microcontroller that is already available in most devices, for example, you can save yourself the additional hardware, since debouncing in software works practically just as well. It should only be noted that additional computing power and, depending on the implementation, some hardware resources (eg timers) are required. But you have the advantage of short pulses, which obviously can not be a key press but z. B. caused by stray, easy to filter. Edge detectionThere are 4 theoretical states for a push button:
These individual states can now be easily queried / run through. The debouncing takes place through the entire duration of the program. The buttons are connected as active low to use the internal pull-ups. This routine returns the value “1” for the “rising edge” state, otherwise “0” #define TASTERPORT PINC
#define TASTERBIT PINC1
char taster(void)
{
static unsigned char zustand;
char rw = 0;
if ( state == 0 && ! ( KEY PORT & ( 1 << KEYBIT ))) // key is pressed (rising edge)
{
state = 1 ;
rw = 1 ;
}
else if ( state == 1 && ! ( BUTTON PORT & ( 1 << BUTTON BIT ))) // button is held
{
state = 2 ;
rw = 0;
}
else if ( state == 2 && ( KEY PORT & ( 1 << KEYBIT ))) //
key is released (falling edge) {
state = 3 ;
rw = 0 ;
}
else if ( state == 3 && ( BUTTON PORT & ( 1 << BUTTON BIT ))) // button released
{
state = 0 ;
rw = 0;
}
return rw;
}
An extension so that a button is recognized for as long as required can be implemented very simply as follows: // state can either be detected as held for the first time or every other time
else if ((( state == 1 ) || ( state == 2 )) && ! ( TASTERPORT & ( 1 << TASTERBIT ))) / / Button is held
{
state = 2 ;
rw = 0 ;
}
On hold procedureIf a microcontroller is to be used to count how often a contact or a relay is switched, the bouncing of the contact must be taken into account precisely – and differentiated from a desired multiple switching, as otherwise incorrect impulses may be counted or real switching processes may be skipped. This must be taken into account when writing the program with regard to scanning the contact. In the following simple example of a debouncing, it should be noted that the AVR waits 200ms when a button is pressed, i.e. lies idle. A different method should be used for time-critical applications (e.g. querying the key states in a timer interrupt service routine). #include <avr/io.h>
#include <inttypes.h>
#ifndef F_CPU
#warning "F_CPU was not yet defined, is now defined with 3686400"
#define F_CPU 3686400UL / * quartz with 3.6864 Mhz * /
#endif
#include <util/delay.h> /* bei alter avr-libc: #include <avr/delay.h> */
/ * Simple function for debouncing a button * /
inline uint8_t debounce ( volatile uint8_t * port , uint8_t pin )
{
if ( ! ( * Port & ( 1 << pin )) )
{
/ * pin was pulled to ground, wait 100ms * /
_delay_ms ( 50 ); // maximum value of the parameter at _delay_ms
_delay_ms ( 50 ); // note, cf. Documentation of the avr-libc
if ( * port & ( 1 << pin ) )
{
/ * Give user time to release the button * /
_delay_ms ( 50 );
_delay_ms ( 50 );
return 1 ;
}
}
return 0 ;
}
int main ( void )
{
DDRB & = ~ ( 1 << PB0 ); / * PIN PB0 on input push button) * /
PORTB | = ( 1 << PB0 ); / * Activate pull-up resistor * /
...
if ( debounce ( & PINB , PB0 ))
{
/ * If button on PIN PB0 is pressed * /
/ * Switch LED on or off at port PD7: * /
PORTD = PORTD ^ ( 1 << PD7 );
}
...
}
Unfortunately, the above routine has several disadvantages:
A similarly easy-to-use routine, but without all of these disadvantages, can be found in the forum thread debouncing for beginners The DEBOUNCE command in the BASIC dialect BASCOM for AVR is also programmed according to the hold pattern method. The waiting time is 25 ms as standard, but can be overwritten by the user. See BASCOM online manual for DEBOUNCE . A C implementation for a key query with a hold pattern can be found in the article AVR-GCC-Tutorial: IO registers as parameters and variables . The disadvantage of this method is that the controller is blocked by the waiting loop. Implementation with a timer interrupt is cheaper. Waiting loop variant with mask and pointer (according to Christian Riggenbach)Here is another function for debouncing buttons: With the additional code, a debouncing time of 1-3ms on average (at least 8 * 150µs = 1ms) can be achieved. Basically, the function checks the level of the pins on a specific port. If the level was constant 8 times, the loop is exited. This function can be used very well to request buttons in an infinite loop because, as mentioned, it has a short waiting time. void entprellung( volatile uint8_t *port, uint8_t maske ) {
uint8_t port_puffer;
uint8_t entprellungs_puffer;
for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
entprellungs_puffer<<=1;
port_puffer = *port;
_delay_us(150);
if( (*port & maske) == (port_puffer & maske) )
entprellungs_puffer |= 0x01;
}
}
The function is called as follows: // Bugfix 20100414
// http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass
debouncing ( & PINB , ( 1 << PINB2 ) ); // wait for bouncing
if necessary ( PINB & ( 1 << PINB2 ) ) // then read in a stable value
{
// do something
else }
{ // do something else }
Any value can be transferred as a mask. It prevents buttons that are not used from negatively influencing the debounce time. Debounce macro by Peter DanneggerPeter Dannegger described the following simplified debouncing procedure in “Debouncing for Beginners” . The macro works in the original version with active low switches, but can be easily adapted for active high switches ( Tasty Reloaded ). /************************************************************************/
/* */
/* Not so powerful Debouncing Example */
/* No Interrupt needed */
/* */
/* Author: Peter Dannegger */
/* */
/************************************************************************/
// Target: ATtiny13
#include <avr/io.h>
#define F_CPU 9.6e6
#include <util/delay.h>
#define debounce( port, pin ) \
({ \
static uint8_t flag = 0; /* new variable on every macro usage */ \
uint8_t i = 0; \
\
if( flag ){ /* check for key release: */ \
for(;;){ /* loop ... */ \
if( !(port & 1<<pin) ){ /* ... until key pressed or ... */ \
i = 0; /* 0 = bounce */ \
break; \
} \
_delay_us( 98 ); /* * 256 = 25ms */ \
if( --i == 0 ){ /* ... until key >25ms released */ \
flag = 0; /* clear press flag */ \
i = 0; /* 0 = key release debounced */ \
break; \
} \
} \
}else{ /* else check for key press: */ \
for(;;){ /* loop ... */ \
if( (port & 1<<pin) ){ /* ... until key released or ... */ \
i = 0; /* 0 = bounce */ \
break; \
} \
_delay_us( 98 ); /* * 256 = 25ms */ \
if( --i == 0 ){ /* ... until key >25ms pressed */ \
flag = 1; /* set press flag */ \
i = 1; /* 1 = key press debounced */ \
break; \
} \
} \
} \
i; /* return value of Macro */ \
})
/*
Testapplication
*/
int main(void)
{
DDRB &= ~(1<<PB0);
PORTB |= 1<<PB0;
DDRB |= 1<<PB2;
DDRB &= ~(1<<PB1);
PORTB |= 1<<PB1;
DDRB |= 1<<PB3;
for(;;){
if( debounce( PINB, PB1 ) )
PORTB ^= 1<<PB2;
if( debounce( PINB, PB0 ) )
PORTB ^= 1<<PB3;
}
}
If the macro for the same key (pin) is to be called up in several places, a function must be created so that both calls evaluate the same state variable flag [1] : // Hilfsfunktion
uint8_t debounce_C1( void )
{
return debounce(PINC, PC1);
}
// Beispielanwendung
int main(void)
{
DDRB |= 1<<PB2;
DDRB |= 1<<PB3;
DDRC &= ~(1<<PC1);
PORTC |= 1<<PC1; // Pullup für Taster
for(;;){
if( debounce_C1() ) // nicht: debounce(PINC, PC1)
PORTB ^= 1<<PB2;
if( debounce_C1() ) // nicht: debounce(PINC, PC1)
PORTB ^= 1<<PB3;
}
}
Timer procedure (according to Peter Dannegger)Grundroutine (AVR Assembler)See also: Forum benefits
In addition, 8 buttons (active low) can be processed at the same time, so they can all be pressed at exactly the same time. Other routines can e.g. B. only process one key, ie the first or last pressed key wins, or it comes out nonsense. The actual reading and debouncing routine is only 8 instructions short. The debounced key state is in the key_state register . With only 2 further instructions, the change from key open to key pressed is recognized and stored in the key_press register . In the example code, 8 LEDs are then switched on and off. Each key corresponds to a bit in the registers, ie processing is done bit by bit with logical operations. For understanding, it is therefore advisable to paint the logic equations with gates for one bit = one key. The registers can be thought of as flip-flops that work with the debounce time as a clock. Ie you can do it z. B. in a GAL22V10. In addition to the individual instructions, all 8 possible combinations of the 3 signals are shown as comments. Example code for AVR (assembler):
.nolist
.include "c:\avr\inc\1200def.inc"
.list
.def save_sreg = r0
.def iwr0 = r1
.def iwr1 = r2
.def key_old = r3
.def key_state = r4
.def key_press = r5
.def leds = r16
.def wr0 = r17
.equ key_port = pind
.equ led_port = portb
rjmp init
.org OVF0addr ;timer interrupt 24ms
in save_sreg, SREG
get8key: ;/old state iwr1 iwr0
mov iwr0, key_old ;00110011 10101010 00110011
in key_old, key_port ;11110000
eor iwr0, key_old ; 11000011
com key_old ;00001111
mov iwr1, key_state ; 10101010
or key_state, iwr0 ; 11101011
and iwr0, key_old ; 00000011
eor key_state, iwr0 ; 11101000
and iwr1, iwr0 ; 00000010
or key_press, iwr1 ;store key press detect
;
; insert other timer functions here
;
out SREG, save_sreg
networks
;-------------------------------------------------------------------------
init:
ldi wr0, 0xFF
out ddrb, wr0
ldi wr0, 1<<CS02 | 1<<CS00 ;divide by 1024 * 256
out TCCR0, wr0
ldi wr0, 1<<TOIE0 ;enable timer interrupt
out TIMSK, wr0
clr key_old
clr key_state
clr key_press
ldi leds, 0xFF
main: cli
eor leds, key_press ;toggle LEDs
clr key_press ;clear, if key press action done
be
out led_port, leds
rjmp main
;-------------------------------------------------------------
Comfort routine (C for AVR)See also: Forum Note If instead of active-low (hibernation high) active-high (hibernation low) is used, a line must be changed see: entire post in the forum , position 1 in the post , ( position 2 in the post * does not have to be changed, since here the polarity has no influence). Note 2 For initialization see forum Functional principle as above plus additional features:
The program is written for avr-gcc / avr-libc, but can also be used with other compilers and microcontrollers with a few adjustments. A port for the AT91SAM7 can be found here (from the ARM MP3 / AAC Player project ). /************************************************************************/
/* */
/* Debouncing 8 Keys */
/* Sampling 4 Times */
/* With Repeat Function */
/* */
/* Author: Peter Dannegger */
/* danni@specs.de */
/* */
/************************************************************************/
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#ifndef F_CPU
#define F_CPU 1000000 // processor clock frequency
#warning kein F_CPU definiert
#endif
#define KEY_DDR DDRB
#define KEY_PORT PORTB
#define KEY_PIN PINB
#define KEY0 0
#define KEY1 1
#define KEY2 2
#define ALL_KEYS (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
#define REPEAT_MASK (1<<KEY1 | 1<<KEY2) // repeat: key1, key2
#define REPEAT_START 50 // after 500ms
#define REPEAT_NEXT 20 // every 200ms
#define LED_DDR DDRA
#define LED_PORT PORTA
#define LED0 0
#define LED1 1
#define LED2 2
volatile uint8_t key_state; // debounced and inverted key state:
// bit = 1: key pressed
volatile uint8_t key_press; // key press detect
volatile uint8_t key_rpt; // key long press and repeat
ISR( TIMER0_OVF_vect ) // every 10ms
{
static uint8_t ct0 = 0xFF, ct1 = 0xFF, rpt;
uint8_t i;
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms
i = key_state ^ ~KEY_PIN; // key changed ?
ct0 = ~( ct0 & i ); // reset or count ct0
ct1 = ct0 ^ (ct1 & i); // reset or count ct1
i &= ct0 & ct1; // count until roll over ?
key_state ^= i; // then toggle debounced state
key_press |= key_state & i; // 0->1: key press detect
if( (key_state & REPEAT_MASK) == 0 ) // check repeat function
rpt = REPEAT_START; // start delay
if( --rpt == 0 ){
rpt = REPEAT_NEXT; // repeat delay
key_rpt |= key_state & REPEAT_MASK;
}
}
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
cli(); // read and clear atomic !
key_mask &= key_press; // read key(s)
key_press ^= key_mask; // clear key(s)
sei();
return key_mask;
}
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported being pressed in subsequent calls
// to this function. This simulates the user repeatedly
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
cli(); // read and clear atomic !
key_mask &= key_rpt; // read key(s)
key_rpt ^= key_mask; // clear key(s)
sei();
return key_mask;
}
///////////////////////////////////////////////////////////////////
//
// check if a key is pressed right now
//
uint8_t get_key_state( uint8_t key_mask )
{
key_mask &= key_state;
return key_mask;
}
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_short( uint8_t key_mask )
{
cli(); // read key state and key press atomic !
return get_key_press( ~key_state & key_mask );
}
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_long( uint8_t key_mask )
{
return get_key_press( get_key_rpt( key_mask ));
}
int main( void )
{
LED_PORT = 0xFF;
LED_DDR = 0xFF;
// Configure debouncing routines
KEY_DDR &= ~ALL_KEYS; // configure key port for input
KEY_PORT |= ALL_KEYS; // and turn on pull up resistors
TCCR0 = (1<<CS02)|(1<<CS00); // divide by 1024
TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5); // preload for 10ms
TIMSK |= 1<<TOIE0; // enable timer interrupt
be ();
while(1){
if( get_key_short( 1<<KEY1 ))
LED_PORT ^= 1<<LED1;
if( get_key_long( 1<<KEY1 ))
LED_PORT ^= 1<<LED2;
// single press and repeat
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
uint8_t i = LED_PORT;
i = (i & 0x07) | ((i << 1) & 0xF0);
if( i < 0xF0 )
i |= 0x08;
LED_PORT = i;
}
}
}
The single-press-and-repeat example does not work in every circuit; the following example should be more universal (single LED on / off): // single press and repeat
if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
LED_PORT ^=0x08;
Newer variant that allows the following functions for a button: https://www.mikrocontroller.net/topic/48465?goto=1753367#1753367 - get_key_press()
- get_key_rpt()
- get_key_press() mit get_key_rpt()
- get_key_short() mit get_key_long()
- get_key_short() mit get_key_long_r() und get_key_rpt_l()
Extension for the detection of two buttons pressed simultaneously: https://www.mikrocontroller.net/topic/48465?goto=1753367#1753367 - get_key_common()
functionalityThe code is based on 8 parallel vertical counters, which are built up via the variables ct0 and ct1 where each bit in ct0 together with the equivalent bit in ct1 forms a 2-bit counter. The code that takes care of the 8 counters is written so that it treats all 8 counters together in parallel. i = key_state ^ ~KEY_PIN; // key changed ?
At this point, i contains a 1 bit for each key that has changed compared to the previous debounced state (keystate). ct0 = ~( ct0 & i ); // reset or count ct0
ct1 = ct0 ^ (ct1 & i); // reset or count ct1
These two instructions decrease the 2-bit counter ct0 / ct1 by 1 for each bit that is set in i. If there is a 0 bit in the corresponding position in i (no change in status), the counter ct0 / ct1 is set to 1 for this bit. The basic state of the counter is as ct0 == 1 and ct1 == 1 (value 3). The counter therefore counts with every ISR call in which the key was identified as having changed compared to keystate ct1 ct0 1 1 // 3 1 0 // 2 0 1 // 1 0 0 // 0 1 1 // 3 i &= ct0 & ct1; // count until roll over ?
A 1 bit is only retained in i where a 1 bit is found in both ct1 and ct0, ie the counter in question could count to 3. The additional rounding with i intercepts the case where a constant counter value of 3 in i leaves a 1 bit. In the end, this means that only a counter change from 0 to 3 leads to a 1 bit at the relevant point in i, but only if there was also a 1 bit in i at this bit position (which in turn was 1 because: a change to the last known debounced state was found at this input port). All in all, this means that a key press is recognized when the key is found 4 times in a row in a different state than the last known debounced key state. At this point i is therefore a vector of 8 bits, each of which provides information as to whether the corresponding key was found several times in succession in the same state that does not match the last known key state. If this is the case, a corresponding change in the key status is registered in key_state key_state ^= i; // then toggle debounced state
and if the corresponding bit in key_state has changed from 0 to 1, this event is evaluated as ‘key has been depressed’. key_press |= key_state & i; // 0->1: key press detect
This debounces the key input. This applies both when a key is pressed and when it is released (so that the bouncer is not confused with the depression of a key when released). The rest of the code then only deals with the further processing of this debounced key state. The code part looks relatively complex due to the use of the many bitwise operations. But if you keep in mind that some of the bit-wise like a ‘parallel if’ are used simultaneously on all 8 bits, a lot will be simplified. On key_press |= key_state & i;
is nothing more than one // test whether bit 0 is set in both key_state and i
// and set bit 0 in key_press if this is the case
if ( ( key_state & ( 1 << 0 ) ) &&
( i & ( 1 << 0 ) )
key_press | = ( 1 << 0 );
// Bit 1
if( ( key_state & ( 1 << 1 ) ) &&
( i & ( 1 << 1 ) )
key_press |= ( 1 << 1 );
// Bit 2
if( ( key_state & ( 1 << 2 ) ) &&
( i & ( 1 << 2 ) )
key_press |= ( 1 << 2 );
...
only performed as a much more compact operation and for all 8 bits simultaneously. The brevity and efficiency of these few lines of code results from the fact that each bit in the variables stands for a key and all 8 (maximum possible) keys go through the operations simultaneously. How the different modes work is explained using timelines: https://www.mikrocontroller.net/topic/48465?goto=1753367#1844458 “Walkthrough” of the different states of the individual variables by pressing a button (avrfreaks.net) http://www.avrfreaks.net/comment/726676#comment-726676 Reduced to just 1 buttonDiscussions in the forum show again and again that many dislike this code because it seems very complicated. The code is not easy to analyze and pulls out all the stops to save both runtime and memory usage. You often hear the argument: I only need a debouncing for 1 button, is there anything easier? Here is the ‘long form’ of the code, as you would write for just 1 key if you used the exact same debouncing method. You can see: There is no witchcraft involved: the last known debounced state of the key is held in key_state. The pin input is compared to this state and if the two differ, a counter is counted down. If this counts down produces an underflow of the counter, the key is considered debounced and if the key is also pressed then this is noted accordingly in key_press. uint8_t key_state;
uint8_t key_counter;
volatile uint8_t key_press;
ISR( ... Overflow ... )
{
uint8_t input = KEY_PIN & ( 1 << KEY0 );
if( input != key_state ) {
key_counter--;
if( key_counter == 0xFF ) {
key_counter = 3;
key_state = input;
if( input )
key_press = TRUE;
}
}
else
key_counter = 3;
}
uint8_t get_key_press()
{
uint8_t result;
cli();
result = key_press;
key_press = FALSE;
sei();
return result;
}
However, the complete debounce code, as listed above, now impresses in that it is compiled smaller than this more descriptive variant for only 1 key. And all this with increased functionality. Because e.g. an autorepeat is not yet built into this code. And at the latest when you want to debounce a 2nd key, the SRAM memory consumption of this long form is higher than that of the original for 8 keys. It follows that even for just 1 key, the original routine is the better choice. And because of the complexity, one question: Are you able to write an efficient sqrt () function like the one you find in the standard C library? No? Then, according to your reasoning, you shouldn’t actually use the library function sqrt (), instead you would have to write a root function yourself. Self-saturating filter (after Jürgen Schuhmacher)By using discrete signal analysis in software, the functionality of a simple debouncing with a resistor, a capacitor and a Schmitt trigger can be simulated as in hardware by using an abstract IIR filter that emulates a capacitor charging curve. With the rule Y (t) = k Y (t-1) + input, a simple filter is generated that sluggishly follows the input value. If a certain value is exceeded, the output signal is switched with a simple query. For assembler and VHDL with FPGAs, the following representation is suitable due to the easy-to-implement binary operations with a resolution of the filter value memory of only 8 bits: Value_New = Value_Alt – Value_Alt / 16 + 16 * (key = True). The filter value then maps the attenuated course of the input (flanked) and can suppress bouncing up to the limit for fast touching. The output value is then simply the most significant bit of the filter value. To do this, the signal from the button should ideally be sampled 10-20 times faster than the highest desired typing speed. It is possible to scan even faster, but it leads to more bits being required for the filter. The Schmittrigger function can be formed by outputting a 1 at the output when, for example, a 55% limit is exceeded and a 0 when the 45% limit is undershot. The old value is kept in the intermediate area. The real limits of this hysteresis must be adapted to the application, since too narrow limits would otherwise be too sensitive to faults. Simple mean filter (according to Lothar Miller)For digital circuits or PLDs, a FIR filter with flip-flops in a row is recommended. You push the input signal into a flip-flop chain and switch above the middle: SignalInput -> FF1 -> FF2 -> FF3 -> FF4 -> FF5 -> FF6 -> FF7 -> FF8 If all FFs = 1 (sum of FFs = 8) then SignalOutput = 1 This method can be mapped very simply in logic, because only one NOR or AND gate is required for the calculation of the output. However, the real signal must be sampled slowly enough so that the filter period exceeds the bounce time to prevent an 8×1 from being seen in the middle of a passive phase of a bounce process. This makes the interation relatively slow. Comparison of the procedures
Links on the subject |