added 'debounce' HAL module to debounce mechanical switch signals
This commit is contained in:
parent
9328e59aca
commit
aaf422b8da
3 changed files with 390 additions and 0 deletions
Binary file not shown.
|
|
@ -10,6 +10,7 @@ HEADERS =
|
||||||
PRIVHDRS =
|
PRIVHDRS =
|
||||||
|
|
||||||
RT_SRCS = \
|
RT_SRCS = \
|
||||||
|
debounce.c \
|
||||||
encoder.c \
|
encoder.c \
|
||||||
stepgen.c \
|
stepgen.c \
|
||||||
freqgen.c \
|
freqgen.c \
|
||||||
|
|
@ -18,6 +19,7 @@ pid.c \
|
||||||
supply.c
|
supply.c
|
||||||
|
|
||||||
LIBS = \
|
LIBS = \
|
||||||
|
$(RTLIB_DIR)/debounce.o \
|
||||||
$(RTLIB_DIR)/encoder.o \
|
$(RTLIB_DIR)/encoder.o \
|
||||||
$(RTLIB_DIR)/stepgen.o \
|
$(RTLIB_DIR)/stepgen.o \
|
||||||
$(RTLIB_DIR)/freqgen.o \
|
$(RTLIB_DIR)/freqgen.o \
|
||||||
|
|
|
||||||
388
src/hal/components/debounce.c
Normal file
388
src/hal/components/debounce.c
Normal file
|
|
@ -0,0 +1,388 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Description: debounce.c
|
||||||
|
* Input debouncer/filter for HAL bit signals.
|
||||||
|
*
|
||||||
|
* See the "Users Manual" at emc2/docs/Hal_Introduction.pdf
|
||||||
|
*
|
||||||
|
* This is a HAL component that can be used to debounce or filter
|
||||||
|
* noisy digital signals such as those from mechanical switches.
|
||||||
|
* It is a realtime component.
|
||||||
|
*
|
||||||
|
* It supports up to 8 groups of debounce filters. Each group
|
||||||
|
* can have any number of filters, subject to total HAL memory
|
||||||
|
* limits. All the filters in a group run at the same sample
|
||||||
|
* rate and have the same delay (which determines the amount of
|
||||||
|
* filtering).
|
||||||
|
*
|
||||||
|
* There is one function for each group of filters, called
|
||||||
|
* 'debounce.G', where G is the group number. The function
|
||||||
|
* needs to be called from a realtime thread, which sets the
|
||||||
|
* sample rate for all the filters in that group.
|
||||||
|
*
|
||||||
|
* There is one parameter for each group, called 'debounce.G.delay'.
|
||||||
|
* It is an integer, and sets the delay time (in samples) of each
|
||||||
|
* filter in the group.
|
||||||
|
*
|
||||||
|
* Each individual filter exports two pins, 'debounce.G.F.in'
|
||||||
|
* and 'debounce.G.F.out'. G is the group, and F is the
|
||||||
|
* filter number within the group.
|
||||||
|
*
|
||||||
|
*********************************************************************
|
||||||
|
*
|
||||||
|
* Author: John Kasunich (jmkasunich AT att DOT net)
|
||||||
|
* License: GPL Version 2
|
||||||
|
* Created on: 2004/06/12
|
||||||
|
* System: Linux
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004 All rights reserved.
|
||||||
|
*
|
||||||
|
* Last change:
|
||||||
|
* $Revision$
|
||||||
|
* $Author$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef RTAPI
|
||||||
|
#error This is a realtime component only!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <linux/ctype.h> /* isspace() */
|
||||||
|
#include "rtapi.h" /* RTAPI realtime OS API */
|
||||||
|
#include "rtapi_app.h" /* RTAPI realtime module decls */
|
||||||
|
#include "hal.h" /* HAL public API decls */
|
||||||
|
|
||||||
|
#ifdef MODULE
|
||||||
|
/* module information */
|
||||||
|
MODULE_AUTHOR("John Kasunich");
|
||||||
|
MODULE_DESCRIPTION("Debounce filter for EMC HAL");
|
||||||
|
#ifdef MODULE_LICENSE
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
#endif /* MODULE_LICENSE */
|
||||||
|
static char *cfg = "1"; /* config string, default = 1 filter */
|
||||||
|
MODULE_PARM(cfg, "s");
|
||||||
|
MODULE_PARM_DESC(cfg, "config string");
|
||||||
|
#endif /* MODULE */
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* STRUCTURES AND GLOBAL VARIABLES *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
/** uncomment this line to export filter internal state variable */
|
||||||
|
/* #define EXPORT_STATE */
|
||||||
|
|
||||||
|
/** This structure contains the runtime data for a single filter. */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hal_bit_t *in; /* pin: input */
|
||||||
|
hal_bit_t *out; /* pin: output */
|
||||||
|
hal_s32_t state; /* parameter*: internal state */
|
||||||
|
} debounce_t;
|
||||||
|
|
||||||
|
/* *note - this parameter is only exported if EXPORT_STATE is defined */
|
||||||
|
|
||||||
|
/** This structure contains the runtime data for a group of filters */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int channels; /* number of channels in group */
|
||||||
|
hal_s32_t delay; /* parameter: delay for this group */
|
||||||
|
debounce_t *filter_array; /* pointer to individual filter data */
|
||||||
|
} debounce_group_t;
|
||||||
|
|
||||||
|
/* ptr to array of debounce_group_t structs in shmem, 1 per group */
|
||||||
|
static debounce_group_t *group_array;
|
||||||
|
|
||||||
|
/* other globals */
|
||||||
|
static int comp_id; /* component ID */
|
||||||
|
static int num_groups; /* number of filter groups configured */
|
||||||
|
static int num_filters; /* number of individual filters */
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* LOCAL FUNCTION DECLARATIONS *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
static int parse_group_size(char *cp);
|
||||||
|
static int export_filter(int num, debounce_t * addr, int group_num);
|
||||||
|
static int export_group(int num, debounce_group_t * addr, int group_size);
|
||||||
|
static void debounce(void *arg, long period);
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* INIT AND EXIT CODE *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
#define MAX_GROUP 8
|
||||||
|
#define MAX_TOK (MAX_GROUP)
|
||||||
|
#define MAX_GROUP_SIZE 50
|
||||||
|
|
||||||
|
int rtapi_app_main(void)
|
||||||
|
{
|
||||||
|
char *cp;
|
||||||
|
char *tokens[MAX_TOK];
|
||||||
|
int group_size[MAX_GROUP];
|
||||||
|
int n, retval;
|
||||||
|
|
||||||
|
/* test for config string */
|
||||||
|
if (cfg == 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR, "DEBOUNCE: ERROR: no config string\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* point to config string */
|
||||||
|
cp = cfg;
|
||||||
|
/* break it into tokens */
|
||||||
|
for (n = 0; n < MAX_TOK; n++) {
|
||||||
|
/* strip leading whitespace */
|
||||||
|
while ((*cp != '\0') && (isspace(*cp)))
|
||||||
|
cp++;
|
||||||
|
/* mark beginning of token */
|
||||||
|
tokens[n] = cp;
|
||||||
|
/* find end of token */
|
||||||
|
while ((*cp != '\0') && (!isspace(*cp)))
|
||||||
|
cp++;
|
||||||
|
/* mark end of this token, prepare to search for next one */
|
||||||
|
if (*cp != '\0') {
|
||||||
|
*cp = '\0';
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* mark all groups unused */
|
||||||
|
for (n = 0; n < MAX_GROUP; n++) {
|
||||||
|
group_size[n] = 0;
|
||||||
|
}
|
||||||
|
/* parse config string, results in group_size[] array */
|
||||||
|
num_groups = 0;
|
||||||
|
num_filters = 0;
|
||||||
|
n = 0;
|
||||||
|
while ((num_groups < MAX_GROUP) && (n < MAX_TOK)) {
|
||||||
|
if (tokens[n][0] != '\0') {
|
||||||
|
/* something here, is it a valid group size type? */
|
||||||
|
group_size[num_groups] = parse_group_size(tokens[n]);
|
||||||
|
if ((group_size[num_groups] < 1)
|
||||||
|
|| (group_size[num_groups] > MAX_GROUP_SIZE)) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: bad group size '%s'\n", tokens[n]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
num_filters += group_size[num_groups];
|
||||||
|
num_groups++;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
/* OK, now we've parsed everything */
|
||||||
|
if (num_groups == 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: no channels configured\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* have good config info, connect to the HAL */
|
||||||
|
comp_id = hal_init("debounce");
|
||||||
|
if (comp_id < 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: hal_init() failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* allocate shared memory for filter group array */
|
||||||
|
group_array = hal_malloc(num_groups * sizeof(debounce_group_t));
|
||||||
|
if (group_array == 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: hal_malloc() failed\n");
|
||||||
|
hal_exit(comp_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* export group data */
|
||||||
|
for (n = 0; n < num_groups; n++) {
|
||||||
|
/* export all vars */
|
||||||
|
retval = export_group(n, &(group_array[n]), group_size[n]);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: group %d export failed\n", n);
|
||||||
|
hal_exit(comp_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtapi_print_msg(RTAPI_MSG_INFO,
|
||||||
|
"DEBOUNCE: installed %d groups of debounce filters, %d total\n",
|
||||||
|
num_groups, num_filters);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtapi_app_exit(void)
|
||||||
|
{
|
||||||
|
hal_exit(comp_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* REALTIME DEBOUNCE FUNCTION *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
/** The debounce filter works by incrementing a counter whenver the
|
||||||
|
input is true, and decrementing the counter when it is false.
|
||||||
|
If the counter decrements to zero, the output is set false and
|
||||||
|
the counter ignores further decrements. If the counter increments
|
||||||
|
up to a threshold, the output is set true and the counter ignores
|
||||||
|
further increments. If the counter is between zero and the
|
||||||
|
threshold, the output retains its previous state. The threshold
|
||||||
|
determines the amount of filtering - a threshold of 1 does no
|
||||||
|
filtering at all, and a threshold of N requires a signal to be
|
||||||
|
present for N samples before the output changes state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** this function processes an entire group of filters with the
|
||||||
|
same threshold. */
|
||||||
|
|
||||||
|
static void debounce(void *arg, long period)
|
||||||
|
{
|
||||||
|
debounce_group_t *group;
|
||||||
|
debounce_t *filter;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/* point to filter group */
|
||||||
|
group = (debounce_group_t *) arg;
|
||||||
|
/* first make sure delay is sane */
|
||||||
|
if (group->delay < 0) {
|
||||||
|
group->delay = 1;
|
||||||
|
}
|
||||||
|
/* loop thru filters */
|
||||||
|
for (n = 0; n < group->channels; n++) {
|
||||||
|
/* point at a filter */
|
||||||
|
filter = &(group->filter_array[n]);
|
||||||
|
/* update this filter */
|
||||||
|
if (*(filter->in)) {
|
||||||
|
/* input true, is state at threshold? */
|
||||||
|
if (filter->state < group->delay) {
|
||||||
|
/* no, increment */
|
||||||
|
filter->state++;
|
||||||
|
} else {
|
||||||
|
/* yes, set output */
|
||||||
|
*(filter->out) = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* input false, is state at zero? */
|
||||||
|
if (filter->state > 0) {
|
||||||
|
/* no, decrement */
|
||||||
|
filter->state--;
|
||||||
|
} else {
|
||||||
|
/* yes, clear output */
|
||||||
|
*(filter->out) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* LOCAL FUNCTION DEFINITIONS *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
static int parse_group_size(char *cp)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (*cp == '\0') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* initial value */
|
||||||
|
result = 0;
|
||||||
|
/* parse digits */
|
||||||
|
while (*cp != '\0') {
|
||||||
|
/* if char is a decimal digit, add it to result */
|
||||||
|
if ((*cp >= '0') && (*cp <= '9')) {
|
||||||
|
result *= 10;
|
||||||
|
result += *cp - '0';
|
||||||
|
} else {
|
||||||
|
/* not a valid digit */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* next char */
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int export_group(int num, debounce_group_t * addr, int group_size)
|
||||||
|
{
|
||||||
|
int n, retval, msg;
|
||||||
|
char buf[HAL_NAME_LEN + 2];
|
||||||
|
|
||||||
|
/* This function exports a lot of stuff, which results in a lot of
|
||||||
|
logging if msg_level is at INFO or ALL. So we save the current value
|
||||||
|
of msg_level and restore it later. If you actually need to log this
|
||||||
|
function's actions, change the second line below */
|
||||||
|
msg = rtapi_get_msg_level();
|
||||||
|
rtapi_set_msg_level(RTAPI_MSG_WARN);
|
||||||
|
|
||||||
|
/* allocate shared memory for this filter group */
|
||||||
|
addr->filter_array = hal_malloc(group_size * sizeof(debounce_t));
|
||||||
|
if (addr->filter_array == 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: hal_malloc() failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* export param variable for delay */
|
||||||
|
rtapi_snprintf(buf, HAL_NAME_LEN, "debounce.%d.delay", num);
|
||||||
|
retval = hal_param_s32_new(buf, HAL_WR, &(addr->delay), comp_id);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: '%s' param export failed\n", buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
/* export function */
|
||||||
|
rtapi_snprintf(buf, HAL_NAME_LEN, "debounce.%d", num);
|
||||||
|
retval = hal_export_funct(buf, debounce, addr, 0, 0, comp_id);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: '%s' funct export failed\n", buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* set default parameter values */
|
||||||
|
addr->delay = 5;
|
||||||
|
addr->channels = group_size;
|
||||||
|
|
||||||
|
/* loop to export each filter in group */
|
||||||
|
for (n = 0; n < group_size; n++) {
|
||||||
|
retval = export_filter(n, &(addr->filter_array[n]), num);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: filter %d export failed\n", n);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* restore saved message level */
|
||||||
|
rtapi_set_msg_level(msg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int export_filter(int num, debounce_t * addr, int group_num)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
char buf[HAL_NAME_LEN + 2];
|
||||||
|
|
||||||
|
/* export pin for input */
|
||||||
|
rtapi_snprintf(buf, HAL_NAME_LEN, "debounce.%d.%d.in", group_num, num);
|
||||||
|
retval = hal_pin_bit_new(buf, HAL_RD, &(addr->in), comp_id);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: '%s' pin export failed\n", buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
/* export pin for output */
|
||||||
|
rtapi_snprintf(buf, HAL_NAME_LEN, "debounce.%d.%d.out", group_num, num);
|
||||||
|
retval = hal_pin_bit_new(buf, HAL_WR, &(addr->out), comp_id);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: '%s' pin export failed\n", buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#ifdef EXPORT_STATE
|
||||||
|
/* export parameter containing internal state */
|
||||||
|
rtapi_snprintf(buf, HAL_NAME_LEN, "debounce.%d.%d.state", group_num, num);
|
||||||
|
retval = hal_param_s32_new(buf, HAL_RD, &(addr->state), comp_id);
|
||||||
|
if (retval != 0) {
|
||||||
|
rtapi_print_msg(RTAPI_MSG_ERR,
|
||||||
|
"DEBOUNCE: ERROR: '%s' param export failed\n", buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* set initial parameter and pin values */
|
||||||
|
addr->state = 0;
|
||||||
|
*(addr->out) = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue