General library for debouncing (filtering) of digital inputs
General library for debouncing (filtering) of digital inputs.
Some programmers use simple straightforward solution like this:
tmp_inp = get_input();
wait(xxx);
if (tmp_inp == get_input())
..........
This library solves the problem much more generally. The requirements are as follows:
- multiplatform
- non-blocking
- applicable to set of inputs
- filtering of both transition (0->1 and 1->0)
- user defined values for minimal Ton and Toff
- allowing separate times for each input
- possibility to use multiple instances (e.g. for more sets of inputs with different requirements)
FAQ:
Q: Why types like "uint_fast8_t" are used?
A: Same library can be used on 8-bit AVR as well as on 32 bit ARM. This type (defined in stdint.h) is as fast as possible on all platforms (e.g. on ARM it should be 32 bit, on AVR 8 bit).
Q: What is "sys_timer_ticks" variable?
A: It is considered that this variable is incremented in some timer interrupt service routine (e.g. each 1ms). More functions in the main code can use this variable for the timing.
Q: Why there is possibility of multiple instances (pointer to handle as the first parameter)?
A: For example in the system there can be several inputs which require fast response, so debounce_proc must be called very fast. Another set of inputs can have slower response, so debounce_proc will be in slower loop.
Q: Why macro __MAX_SIGNED is used, if there are macros like INT_MAX in standard limits.h header file?
A: Because nl_debouce_time_t can be defined differently on each platform, so general macro is necessary.
/**************************************************************************************/
/* sample usage of the library */
/**************************************************************************************/
//Remark: sys_timer_ticks should be provided for timing by the user.
/* Simplest example with undefined CONFIG_DEBOUNCE_WITH_HANDLE and CONFIG_DEBOUNCE_SEPARATE_TIMES */
#include <debounce.h>
#define INPUTS_NUM 12
nl_debouce_time_t inp_times[ INPUTS_NUM ];
nl_debouce_time_t filt_time = 10; //same value will be used for t_on and t_off
int main(void)
{
nl_inp_t inp_state, filtered_inp_state;
debounce_init( inp_times, INPUTS_NUM, &filt_time, &filt_time,
(nl_ticks_t*)&sys_timer_ticks );
while(1)
{
//user defined function, which return actual state of all inputs as bit array
inp_state = get_input_state();
debounce_proc(&inp_state, &filtered_inp_state);
//do something with filtered_inp_state
//...............
//main program functionality
//...............
}
}
/* More complex example with defined CONFIG_DEBOUNCE_WITH_HANDLE and CONFIG_DEBOUNCE_SEPARATE_TIMES */
#include <debounce.h>
#define INPUTS_NUM 5
struct debounce_state_s inp_dbnc_s;
nl_debouce_time_t inp_times[ INPUTS_NUM ];
nl_debouce_time_t t_on[ INPUTS_NUM ] = {10,10,20,20,50};
nl_debouce_time_t t_off[ INPUTS_NUM ] = {5,5,10,10,50};
int main(void)
{
nl_inp_t inp_state, filtered_inp_state;
debounce_init( &inp_dbnc_s, inp_times, INPUTS_NUM,
t_on, t_off, (nl_ticks_t*)&sys_timer_ticks );
while(1)
{
//user defined function, which return actual state of all inputs as bit array
inp_state = get_input_state();
debounce_proc(&inp_dbnc_s, &inp_state, &filtered_inp_state);
//do something with filtered_inp_state
//...............
//main program functionality
//...............
}
}
/**************************************************************************************/
/* debounce library header file "debounce.h" */
/**************************************************************************************/
#ifndef _DEBOUNCE_H_
#define _DEBOUNCE_H_
#include <stdint.h>
/* because library is multiplatform, following types are defined in separate file */
#include "nlib_types.h"
/* examaple of types definition in "nlib_types.h" */
//typedef uint32_t nl_ticks_t;
//typedef int16_t nl_debouce_time_t; //so maximum filter time is 32767ms (considering period of ticks 1ms)
//typedef uint32_t nl_inp_t; //up to 32 inputs can be handled
/* in general case following macros should be provided in this header file, or can be defined directly */
#include <debounce_config.h>
//#define CONFIG_DEBOUNCE_WITH_HANDLE
//#define CONFIG_DEBOUNCE_SEPARATE_TIMES
#ifdef CONFIG_DEBOUNCE_WITH_HANDLE
#define DEBOUNCE_STRUCT_PAR struct debounce_state_s * debounce_state,
#else
#define DEBOUNCE_STRUCT_PAR
#endif
typedef struct debounce_state_s
{
const nl_ticks_t* ticks; //pointer to timing variable (is incremented e.g. each 1ms)
nl_ticks_t old_ticks;
uint_fast8_t inp_num; //number of inputs
const nl_debouce_time_t *debounce_on_time,*debounce_off_time; //tables with desired filter times
nl_debouce_time_t* inp_times; //actual time ON/OFF - non-negative values = ON, negative = OFF
}
debounce_state_t;
void debounce_init(DEBOUNCE_STRUCT_PAR nl_debouce_time_t* inp_tim, uint_fast8_t num, const nl_debouce_time_t *dton,const nl_debouce_time_t *dtoff, const nl_ticks_t* ticks);
uint_fast8_t debounce_proc(DEBOUNCE_STRUCT_PAR const nl_inp_t* act_inp_state, nl_inp_t* debounced_inp_state);
#endif /*_DEBOUNCE_H_*/
/**************************************************************************************/
/* debounce library source file "debounce.c" */
/**************************************************************************************/
#include "debounce.h"
#include <string.h>
//allow using multiple instances
#ifndef CONFIG_DEBOUNCE_WITH_HANDLE
struct debounce_state_s _debounce_state_;
struct debounce_state_s * debounce_state = &_debounce_state_;
#endif
/* allow different times for each input */
#ifndef CONFIG_DEBOUNCE_SEPARATE_TIMES
#define _debounce_times_idx_ 0
#else
#define _debounce_times_idx_ i
#endif
//maco trick to find maximum value of given signed integer type "http://www.fefe.de/intof.html"
#define __HALF_MAX_SIGNED(type) ((type)1 << (sizeof(type)*8-2))
#define __MAX_SIGNED(type) (__HALF_MAX_SIGNED(type) - 1 + __HALF_MAX_SIGNED(type))
/*
Init function of the library
DEBOUNCE_STRUCT_PAR - depending on "CONFIG_DEBOUNCE_WITH_HANDLE": nothing, or pointer to handle
inp_tim - array of variables for storing of state for each input (number of elements must be the same as number of inputs!)
num - number of inputs
dton - depending on "CONFIG_DEBOUNCE_SEPARATE_TIMES": pointer to sigle value (minimal ON time), or array of times
dtoff - depending on "CONFIG_DEBOUNCE_SEPARATE_TIMES": pointer to sigle value (minimal OFF time), or array of times
ticks - pointer to variable, which is periodicaly incremented
*/
void debounce_init(DEBOUNCE_STRUCT_PAR nl_debouce_time_t* inp_tim, uint_fast8_t num, const nl_debouce_time_t *dton, const nl_debouce_time_t *dtoff ,const nl_ticks_t* ticks)
{
debounce_state-> inp_times=inp_tim;
debounce_state-> inp_num=num;
debounce_state-> debounce_on_time=dton;
debounce_state-> debounce_off_time=dtoff;
debounce_state-> ticks=ticks;
debounce_state-> old_ticks=*ticks;
memset(inp_tim,0,sizeof(*inp_tim)*num);
inp_tim[0]=__MAX_SIGNED(nl_debouce_time_t); //this is used later to evaluate first iteration after start
}
/*
This is core function of the library
DEBOUNCE_STRUCT_PAR - depending on "CONFIG_DEBOUNCE_WITH_HANDLE": nothing, or pointer to handle
act_inp_state - pointer to variable with actual state of all inputs (1 bit for each input)
debounced_inp_state - resulting state after filtering
return - 0=no change, 1=some input(s) are changed
*/
uint_fast8_t debounce_proc(DEBOUNCE_STRUCT_PAR const nl_inp_t* act_inp_state, nl_inp_t* debounced_inp_state)
{
uint_fast8_t i,change=0;
nl_inp_t mask=1;
nl_ticks_t tic_diff;
tic_diff=(nl_ticks_t) (*(debounce_state-> ticks) - debounce_state-> old_ticks);
debounce_state-> old_ticks = *(debounce_state-> ticks);
if ((debounce_state-> inp_times)[0] == __MAX_SIGNED(nl_debouce_time_t)) //evaluate, if it is a first iteration
{
*debounced_inp_state=*act_inp_state;
for(i=0; i<debounce_state-> inp_num ;i++)
(debounce_state-> inp_times)[i]=0;
return 0;
}
for(i=0; i<debounce_state-> inp_num ;i++)
{
if ( *act_inp_state & mask) //actual state is ON
{
if ((debounce_state-> inp_times)[i] >= 0) //and last state was ON
{
if (((debounce_state-> inp_times)[i] + (nl_debouce_time_t) tic_diff) < debounce_state-> debounce_on_time[_debounce_times_idx_])
{
(debounce_state-> inp_times)[i] += (nl_debouce_time_t) tic_diff; //filter time not elapsed
}
else
{ //filter time elapsed
if (!( *debounced_inp_state & mask))
{
*debounced_inp_state |= mask;
change=1;
}
}
}
else (debounce_state-> inp_times)[i] = 0;
}
else //actual state is OFF
{
if (debounce_state-> inp_times[i] < 0) //and last state was OFF
{
if ( (nl_debouce_time_t)(((debounce_state-> inp_times)[i] - (nl_debouce_time_t) tic_diff)) >= (-1*debounce_state-> debounce_off_time[_debounce_times_idx_]))
{
(debounce_state-> inp_times)[i] -= (nl_debouce_time_t) tic_diff; //filter time not elapsed
}
else
{ //filter time elapsed
if ( *debounced_inp_state & mask)
{
*debounced_inp_state &= ~mask;
change=1;
}
}
}
else (debounce_state-> inp_times)[i] = -1;
}
mask=(nl_inp_t) mask<<1;
}
return change;
}