Mar 312020
 

https://wepresent.wetransfer.com/story/arif-al-nomay-corrupted-files/

 

Unexpected stories about creativity
Told by WeTransfer

Arif Al Nomay I just want the world to know about us

When a power cut and a computer malfunction caused Yemeni photographer Arif Al Nomay to lose years of work, he anxiously hunted for anything that might still be there. To his surprise, a set of photos taken at a Summer Festival in his country’s capital had survived, but they had been altered, their colors skewed and the details hard to distinguish. Since the festival took place the year before Yemen descended into war, the series would become a symbol for the pieces of everyday life that were lost as a result.

Words by Alex Kahl.

In 2014 Arif Al Nomay traveled with his camera to a park called The Seventy in Sanaa, the capital city of his home country of Yemen. He had been hired to take photographs at an annual festival, a celebration of the heritage that characterizes many of the country’s different regions.

 Posted by at 1:02 pm
Mar 242020
 

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

Development

From the Mikrocontroller.net article collection, with contributions from various authors (see version history)

Problem Description

Mechanical 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: Entprellen.png

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:

Hardwareentprellung

Bounce-free switches

As 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 switch

A 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.

Debounce buttons with NAND-RS flip-flop

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-flop

If you don’t have a flip-flop at hand, you can also use this circuit.

Changer debounce with capacitor

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:

dUdt=IC

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:

C=IdtdU=1μA10ms0,5V20 F

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 button

Even 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.

Debounce buttons with RC debouncer

Emerging voltage curve


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:

UC(t)=U0etR2C1

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.

UC(t) Uth

By changing the formula:

R2=tC1⋅ UthU0)

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:

UC(t)=U0(1et(R1+R2)C1)

With U_th = U_C, the changeover to (R_1 + R_2) results in:

R1+R2=tC1⋅ ( 1 – UthU0)

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.

Softwareentprellung

In 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 detection

There are 4 theoretical states for a push button:

  • 1. was not pressed and is not pressed
  • 2. was not pressed and is pressed (rising edge)
  • 3. was pressed and is still pressed
  • 4. was pressed and is no longer pressed (falling edge)

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 procedure

If 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:

  • it only detects letting go (unergonomic)
  • it always delays the main loop by 100ms when the button is pressed
  • it loses keystrokes the more the mainloop has to do.

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 Dannegger

Peter 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

  • particularly short code
  • fast

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:

  • Can save keys by different actions with short or long press
  • Repeat function, e.g. B. for entering values

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()
functionality

The code is based on 8 parallel vertical counters, which are built up via the variables ct0 and ct1

8 vertical counters in 2 8-bit variables

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 button

Discussions 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.

Debouncing with IIR filter.gif

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
If all FFs = 0 (sum of FFs = 0) then SignalOutput = 0

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

  • HW – “debounced switch”: Very expensive, large design, subject to wear, low durability
  • HW – “switch”: requires more complex switches, requires electronics
  • HW – “switch without FF”: requires more complex switches and a small capacitor
  • HW – “capacitor debouncing”: requires a little more space, copes with bad switches
  • SW – edge method:
  • SW – holding pattern: Due to the holding pattern, a not insignificant delay in the code. Especially when several buttons are to be monitored, not unproblematic
  • SW timer: universal functionality that impresses with its low memory consumption, low computing time consumption and good function. At first glance, the ‘consumption’ of a timer looks worse than it is, because most programs use a basic timer for the time control of the program anyway, which can also be used for debouncing the buttons.
  • SW filter: very little space required in FPGAs, relatively good effect
  • SW filter 2: very small space requirement, good effect

Links on the subject

 Posted by at 6:47 pm
Mar 242020
 

 

SerialSend

SerialSend is a little command line application I created to send text strings via a serial port. I mainly use it to send information to microcontroller circuits via a USB-to-serial converter, so it’s designed to work well in that context.

  • Copyright © 2010-2019 Ted Burke, All Rights Reserved.

 Posted by at 2:31 pm