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 }