/ src / hal / components / debounce.c
debounce.c
  1  /********************************************************************
  2  * Description: debounce.c
  3  *   Input debouncer/filter for HAL bit signals.
  4  *
  5  *   See the "Users Manual" at emc2/docs/Hal_Introduction.pdf
  6  *
  7  *   This is a HAL component that can be used to debounce or filter
  8  *   noisy digital signals such as those from mechanical switches.
  9  *   It is a realtime component.
 10  *
 11  *   It supports up to 8 groups of debounce filters.  Each group
 12  *   can have any number of filters, subject to total HAL memory
 13  *   limits.  All the filters in a group run at the same sample
 14  *   rate and have the same delay (which determines the amount of
 15  *   filtering).
 16  *
 17  *   There is one function for each group of filters, called
 18  *   'debounce.G', where G is the group number.  The function
 19  *   needs to be called from a realtime thread, which sets the
 20  *   sample rate for all the filters in that group.
 21  *
 22  *   There is one parameter for each group, called 'debounce.G.delay'.
 23  *   It is an integer, and sets the delay time (in samples) of each
 24  *   filter in the group.
 25  *
 26  *   Each individual filter exports two pins, 'debounce.G.F.in'
 27  *   and 'debounce.G.F.out'.  G is the group, and F is the
 28  *   filter number within the group.
 29  *
 30  *********************************************************************
 31  *
 32  * Author: John Kasunich (jmkasunich AT att DOT net)
 33  * License: GPL Version 2
 34  * Created on: 2004/06/12
 35  * System: Linux
 36  *
 37  * Copyright (c) 2004 All rights reserved.
 38  *
 39  * Last change:
 40  *
 41  ********************************************************************/
 42  
 43  #include "rtapi_ctype.h"	/* isspace() */
 44  #include "rtapi.h"		/* RTAPI realtime OS API */
 45  #include "rtapi_app.h"		/* RTAPI realtime module decls */
 46  #include "hal.h"		/* HAL public API decls */
 47  
 48  /* module information */
 49  #define MAX_GROUP 8
 50  #define STRINGIZE(x) #x
 51  #define MAX_GROUP_STR STRINGIZE(MAX_GROUP)
 52  MODULE_AUTHOR("John Kasunich");
 53  MODULE_DESCRIPTION("Debounce filter for EMC HAL");
 54  MODULE_LICENSE("GPL");
 55  int cfg[MAX_GROUP] = {0,};
 56  RTAPI_MP_ARRAY_INT(cfg,MAX_GROUP,"Group size for up to "MAX_GROUP_STR" groups");
 57  
 58  /***********************************************************************
 59  *                STRUCTURES AND GLOBAL VARIABLES                       *
 60  ************************************************************************/
 61  
 62  /** uncomment this line to export filter internal state variable */
 63  /* #define EXPORT_STATE */
 64  
 65  /** This structure contains the runtime data for a single filter. */
 66  
 67  typedef struct {
 68      hal_bit_t *in;		/* pin: input */
 69      hal_bit_t *out;		/* pin: output */
 70      hal_s32_t state;		/* parameter*: internal state */
 71  } debounce_t;
 72  
 73  /*  *note - this parameter is only exported if EXPORT_STATE is defined */
 74  
 75  /** This structure contains the runtime data for a group of filters */
 76  
 77  typedef struct {
 78      int channels;		/* number of channels in group */
 79      hal_s32_t delay;		/* parameter: delay for this group */
 80      debounce_t *filter_array;	/* pointer to individual filter data */
 81  } debounce_group_t;
 82  
 83  /* ptr to array of debounce_group_t structs in shmem, 1 per group */
 84  static debounce_group_t *group_array;
 85  
 86  /* other globals */
 87  static int comp_id;		/* component ID */
 88  static int num_groups;		/* number of filter groups configured */
 89  static int num_filters;		/* number of individual filters */
 90  
 91  /***********************************************************************
 92  *                  LOCAL FUNCTION DECLARATIONS                         *
 93  ************************************************************************/
 94  
 95  static int export_filter(int num, debounce_t * addr, int group_num);
 96  static int export_group(int num, debounce_group_t * addr, int group_size);
 97  static void debounce(void *arg, long period);
 98  
 99  /***********************************************************************
100  *                       INIT AND EXIT CODE                             *
101  ************************************************************************/
102  
103  #define MAX_GROUP_SIZE 50
104  
105  int rtapi_app_main(void)
106  {
107      int retval, n;
108  
109      /* count number of groups and filters */
110      num_groups = 0;
111      num_filters = 0;
112  
113      while ((num_groups < MAX_GROUP) && (cfg[num_groups] != 0)) {
114          if ((cfg[num_groups] < 1)
115              || (cfg[num_groups] > MAX_GROUP_SIZE)) {
116              rtapi_print_msg(RTAPI_MSG_ERR,
117                  "DEBOUNCE: ERROR: bad group size '%d'\n", cfg[num_groups]);
118              return -1;
119          }
120          num_filters += cfg[num_groups];
121          num_groups++;
122      }
123      /* OK, now we've counted everything */
124      if (num_groups == 0) {
125  	rtapi_print_msg(RTAPI_MSG_ERR,
126  	    "DEBOUNCE: ERROR: no channels configured\n");
127  	return -1;
128      }
129      /* have good config info, connect to the HAL */
130      comp_id = hal_init("debounce");
131      if (comp_id < 0) {
132  	rtapi_print_msg(RTAPI_MSG_ERR,
133  	    "DEBOUNCE: ERROR: hal_init() failed\n");
134  	return -1;
135      }
136      /* allocate shared memory for filter group array */
137      group_array = hal_malloc(num_groups * sizeof(debounce_group_t));
138      if (group_array == 0) {
139  	rtapi_print_msg(RTAPI_MSG_ERR,
140  	    "DEBOUNCE: ERROR: hal_malloc() failed\n");
141  	hal_exit(comp_id);
142  	return -1;
143      }
144      /* export group data */
145      for (n = 0; n < num_groups; n++) {
146  	/* export all vars */
147  	retval = export_group(n, &(group_array[n]), cfg[n]);
148  	if (retval != 0) {
149  	    rtapi_print_msg(RTAPI_MSG_ERR,
150  		"DEBOUNCE: ERROR: group %d export failed\n", n);
151  	    hal_exit(comp_id);
152  	    return -1;
153  	}
154      }
155      rtapi_print_msg(RTAPI_MSG_INFO,
156  	"DEBOUNCE: installed %d groups of debounce filters, %d total\n",
157  	num_groups, num_filters);
158      hal_ready(comp_id);
159      return 0;
160  }
161  
162  void rtapi_app_exit(void)
163  {
164      hal_exit(comp_id);
165  }
166  
167  /***********************************************************************
168  *                     REALTIME DEBOUNCE FUNCTION                       *
169  ************************************************************************/
170  
171  /** The debounce filter works by incrementing a counter whenver the
172      input is true, and decrementing the counter when it is false.
173      If the counter decrements to zero, the output is set false and
174      the counter ignores further decrements.  If the counter increments
175      up to a threshold, the output is set true and the counter ignores
176      further increments.  If the counter is between zero and the
177      threshold, the output retains its previous state.  The threshold
178      determines the amount of filtering - a threshold of 1 does no
179      filtering at all, and a threshold of N requires a signal to be
180      present for N samples before the output changes state.
181  */
182  
183  /** this function processes an entire group of filters with the
184      same threshold. */
185  
186  static void debounce(void *arg, long period)
187  {
188      debounce_group_t *group;
189      debounce_t *filter;
190      int n;
191  
192      /* point to filter group */
193      group = (debounce_group_t *) arg;
194      /* first make sure delay is sane */
195      if (group->delay < 0) {
196  	group->delay = 1;
197      }
198      /* loop thru filters */
199      for (n = 0; n < group->channels; n++) {
200  	/* point at a filter */
201  	filter = &(group->filter_array[n]);
202  	/* update this filter */
203  	if (*(filter->in)) {
204  	    /* input true, is state at threshold? */
205  	    if (filter->state < group->delay) {
206  		/* no, increment */
207  		filter->state++;
208  	    } else {
209  		/* yes, set output */
210  		*(filter->out) = 1;
211  	    }
212  	} else {
213  	    /* input false, is state at zero? */
214  	    if (filter->state > 0) {
215  		/* no, decrement */
216  		filter->state--;
217  	    } else {
218  		/* yes, clear output */
219  		*(filter->out) = 0;
220  	    }
221  	}
222      }
223  }
224  
225  /***********************************************************************
226  *                   LOCAL FUNCTION DEFINITIONS                         *
227  ************************************************************************/
228  
229  static int export_group(int num, debounce_group_t * addr, int group_size)
230  {
231      int n, retval, msg;
232      char buf[HAL_NAME_LEN + 1];
233  
234      /* This function exports a lot of stuff, which results in a lot of
235         logging if msg_level is at INFO or ALL. So we save the current value
236         of msg_level and restore it later.  If you actually need to log this
237         function's actions, change the second line below */
238      msg = rtapi_get_msg_level();
239      rtapi_set_msg_level(RTAPI_MSG_WARN);
240  
241      /* allocate shared memory for this filter group */
242      addr->filter_array = hal_malloc(group_size * sizeof(debounce_t));
243      if (addr->filter_array == 0) {
244  	rtapi_print_msg(RTAPI_MSG_ERR,
245  	    "DEBOUNCE: ERROR: hal_malloc() failed\n");
246  	return -1;
247      }
248      /* export param variable for delay */
249      rtapi_snprintf(buf, sizeof(buf), "debounce.%d.delay", num);
250      retval = hal_param_s32_new(buf, HAL_RW, &(addr->delay), comp_id);
251      if (retval != 0) {
252  	rtapi_print_msg(RTAPI_MSG_ERR,
253  	    "DEBOUNCE: ERROR: '%s' param export failed\n", buf);
254  	return retval;
255      }
256      /* export function */
257      rtapi_snprintf(buf, sizeof(buf), "debounce.%d", num);
258      retval = hal_export_funct(buf, debounce, addr, 0, 0, comp_id);
259      if (retval != 0) {
260  	rtapi_print_msg(RTAPI_MSG_ERR,
261  	    "DEBOUNCE: ERROR: '%s' funct export failed\n", buf);
262  	return -1;
263      }
264      /* set default parameter values */
265      addr->delay = 5;
266      addr->channels = group_size;
267  
268      /* loop to export each filter in group */
269      for (n = 0; n < group_size; n++) {
270  	retval = export_filter(n, &(addr->filter_array[n]), num);
271  	if (retval != 0) {
272  	    rtapi_print_msg(RTAPI_MSG_ERR,
273  		"DEBOUNCE: ERROR: filter %d export failed\n", n);
274  	    return -1;
275  	}
276      }
277      /* restore saved message level */
278      rtapi_set_msg_level(msg);
279      return 0;
280  }
281  
282  static int export_filter(int num, debounce_t * addr, int group_num)
283  {
284      int retval;
285      char buf[HAL_NAME_LEN + 1];
286  
287      /* export pin for input */
288      rtapi_snprintf(buf, sizeof(buf), "debounce.%d.%d.in", group_num, num);
289      retval = hal_pin_bit_new(buf, HAL_IN, &(addr->in), comp_id);
290      if (retval != 0) {
291  	rtapi_print_msg(RTAPI_MSG_ERR,
292  	    "DEBOUNCE: ERROR: '%s' pin export failed\n", buf);
293  	return retval;
294      }
295      /* export pin for output */
296      rtapi_snprintf(buf, sizeof(buf), "debounce.%d.%d.out", group_num, num);
297      retval = hal_pin_bit_new(buf, HAL_OUT, &(addr->out), comp_id);
298      if (retval != 0) {
299  	rtapi_print_msg(RTAPI_MSG_ERR,
300  	    "DEBOUNCE: ERROR: '%s' pin export failed\n", buf);
301  	return retval;
302      }
303  #ifdef EXPORT_STATE
304      /* export parameter containing internal state */
305      rtapi_snprintf(buf, sizeof(buf), "debounce.%d.%d.state", group_num, num);
306      retval = hal_param_s32_new(buf, HAL_RO, &(addr->state), comp_id);
307      if (retval != 0) {
308  	rtapi_print_msg(RTAPI_MSG_ERR,
309  	    "DEBOUNCE: ERROR: '%s' param export failed\n", buf);
310  	return retval;
311      }
312  #endif
313      /* set initial parameter and pin values */
314      addr->state = 0;
315      *(addr->out) = 0;
316      return 0;
317  }