/ src / hal / components / encoder.c
encoder.c
  1  /********************************************************************
  2  * Description:  encoder.c
  3  *               This file, 'encoder.c', is a HAL component that 
  4  *               provides software based counting of quadrature 
  5  *               encoder signals.
  6  *
  7  * Author: John Kasunich
  8  * License: GPL Version 2
  9  *    
 10  * Copyright (c) 2003 All rights reserved.
 11  *
 12  * Last change: 
 13  ********************************************************************/
 14  /** This file, 'encoder.c', is a HAL component that provides software
 15      based counting of quadrature encoder signals.  The maximum count
 16      rate will depend on the speed of the PC, but is expected to exceed
 17      1KHz for even the slowest computers, and may reach 10KHz on fast
 18      ones.  It is a realtime component.
 19  
 20      It supports up to eight counters, with optional index pulses.
 21      The number of counters is set by the module parameter 'num_chan='
 22      when the component is insmod'ed.  Alternatively, use the
 23      names= specifier and a list of unique names separated by commas.
 24      The names= and num_chan= specifiers are mutually exclusive.
 25  
 26      The driver exports variables for each counters inputs and output.
 27      It also exports two functions.  "encoder.update-counters" must be
 28      called in a high speed thread, at least twice the maximum desired
 29      count rate.  "encoder.capture-position" can be called at a much
 30      slower rate, and updates the output variables.
 31  */
 32  
 33  /** Copyright (C) 2003 John Kasunich
 34                         <jmkasunich AT users DOT sourceforge DOT net>
 35  */
 36  
 37  /** This program is free software; you can redistribute it and/or
 38      modify it under the terms of version 2 of the GNU General
 39      Public License as published by the Free Software Foundation.
 40      This library is distributed in the hope that it will be useful,
 41      but WITHOUT ANY WARRANTY; without even the implied warranty of
 42      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 43      GNU General Public License for more details.
 44  
 45      You should have received a copy of the GNU General Public
 46      License along with this library; if not, write to the Free Software
 47      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 48  
 49      THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 50      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 51      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 52      harming persons must have provisions for completely removing power
 53      from all motors, etc, before persons enter any danger area.  All
 54      machinery must be designed to comply with local and national safety
 55      codes, and the authors of this software can not, and do not, take
 56      any responsibility for such compliance.
 57  
 58      This code was written as part of the EMC HAL project.  For more
 59      information, go to www.linuxcnc.org.
 60  */
 61  
 62  #include "rtapi.h"		/* RTAPI realtime OS API */
 63  #include "rtapi_app.h"		/* RTAPI realtime module decls */
 64  #include "rtapi_string.h"
 65  #include "hal.h"		/* HAL public API decls */
 66  
 67  /* module information */
 68  MODULE_AUTHOR("John Kasunich");
 69  MODULE_DESCRIPTION("Encoder Counter for EMC HAL");
 70  MODULE_LICENSE("GPL");
 71  
 72  static int num_chan;
 73  static int default_num_chan=3;
 74  static int howmany;
 75  RTAPI_MP_INT(num_chan, "number of encoder channels");
 76  
 77  #define MAX_CHAN 8
 78  char *names[MAX_CHAN] = {0,};
 79  RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "names of encoder");
 80  
 81  /***********************************************************************
 82  *                STRUCTURES AND GLOBAL VARIABLES                       *
 83  ************************************************************************/
 84  
 85  /* data that is atomically passed from fast function to slow one */
 86  
 87  typedef struct {
 88      char count_detected;
 89      char index_detected;
 90      char latch_detected;
 91      rtapi_s32 raw_count;
 92      rtapi_u32 timestamp;
 93      rtapi_s32 index_count;
 94      rtapi_s32 latch_count;
 95  } atomic;
 96  
 97  /* this structure contains the runtime data for a single counter
 98     u:rw means update() reads and writes the
 99     c:w  means capture() writes the field
100     c:s u:rc means capture() sets (to 1), update() reads and clears
101  */
102  
103  typedef struct {
104      unsigned char state;	/* u:rw quad decode state machine state */
105      unsigned char oldZ;		/* u:rw previous value of phase Z */
106      unsigned char Zmask;	/* u:rc c:s mask for oldZ, from index-ena */
107      hal_bit_t *x4_mode;		/* u:r enables x4 counting (default) */
108      hal_bit_t *counter_mode;	/* u:r enables counter mode */
109      atomic buf[2];		/* u:w c:r double buffer for atomic data */
110      volatile atomic *bp;	/* u:r c:w ptr to in-use buffer */
111      hal_s32_t *raw_counts;	/* u:rw raw count value, in update() only */
112      hal_bit_t *phaseA;		/* u:r quadrature input */
113      hal_bit_t *phaseB;		/* u:r quadrature input */
114      hal_bit_t *phaseZ;		/* u:r index pulse input */
115      hal_bit_t *index_ena;	/* c:rw index enable input */
116      hal_bit_t *reset;		/* c:r counter reset input */
117      hal_bit_t *latch_in;        /* c:r counter latch input */
118      hal_bit_t *latch_rising;    /* u:r latch on rising edge? */
119      hal_bit_t *latch_falling;   /* u:r latch on falling edge? */
120      rtapi_s32 raw_count;		/* c:rw captured raw_count */
121      rtapi_u32 timestamp;		/* c:rw captured timestamp */
122      rtapi_s32 index_count;		/* c:rw captured index count */
123      rtapi_s32 latch_count;		/* c:rw captured index count */
124      hal_s32_t *count;		/* c:w captured binary count value */
125      hal_s32_t *count_latch;     /* c:w captured binary count value */
126      hal_float_t *min_speed;     /* c:r minimum velocity to estimate nonzero */
127      hal_float_t *pos;		/* c:w scaled position (floating point) */
128      hal_float_t *pos_interp;	/* c:w scaled and interpolated position (float) */
129      hal_float_t *pos_latch;     /* c:w scaled latched position (floating point) */
130      hal_float_t *vel;		/* c:w scaled velocity (floating point) */
131      hal_float_t *vel_rpm;   /* rps * 60 for convenience */
132      hal_float_t *pos_scale;	/* c:r pin: scaling factor for pos */
133      hal_bit_t old_latch;        /* value of latch on previous cycle */
134      double old_scale;		/* c:rw stored scale value */
135      double scale;		/* c:rw reciprocal value used for scaling */
136      int counts_since_timeout;	/* c:rw used for velocity calcs */
137  } counter_t;
138  
139  static rtapi_u32 timebase;		/* master timestamp for all counters */
140  
141  /* pointer to array of counter_t structs in shmem, 1 per counter */
142  static counter_t *counter_array;
143  
144  /* bitmasks for quadrature decode state machine */
145  #define SM_PHASE_A_MASK 0x01
146  #define SM_PHASE_B_MASK 0x02
147  #define SM_LOOKUP_MASK  0x0F
148  #define SM_CNT_UP_MASK  0x40
149  #define SM_CNT_DN_MASK  0x80
150  
151  /* Lookup table for quadrature decode state machine.  This machine
152     will reject glitches on either input (will count up 1 on glitch,
153     down 1 after glitch), and on both inputs simultaneously (no count
154     at all)  In theory, it can count once per cycle, in practice the
155     maximum count rate should be at _least_ 10% below the sample rate,
156     and preferrable around half the sample rate.  It counts every
157     edge of the quadrature waveform, 4 counts per complete cycle.
158  */
159  static const unsigned char lut_x4[16] = {
160      0x00, 0x44, 0x88, 0x0C, 0x80, 0x04, 0x08, 0x4C,
161      0x40, 0x04, 0x08, 0x8C, 0x00, 0x84, 0x48, 0x0C
162  };
163  
164  /* same thing, but counts only once per complete cycle */
165  
166  static const unsigned char lut_x1[16] = {
167      0x00, 0x44, 0x08, 0x0C, 0x80, 0x04, 0x08, 0x0C,
168      0x00, 0x04, 0x08, 0x0C, 0x00, 0x04, 0x08, 0x0C
169  };
170  
171  /* look-up table for a one-wire counter */
172  
173  static const unsigned char lut_ctr[16] = {
174     0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175     0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176  };
177  
178  /* other globals */
179  static int comp_id;		/* component ID */
180  
181  /***********************************************************************
182  *                  LOCAL FUNCTION DECLARATIONS                         *
183  ************************************************************************/
184  
185  static int export_encoder(counter_t * addr,char * prefix);
186  static void update(void *arg, long period);
187  static void capture(void *arg, long period);
188  
189  /***********************************************************************
190  *                       INIT AND EXIT CODE                             *
191  ************************************************************************/
192  
193  
194  int rtapi_app_main(void)
195  {
196      int n, retval, i;
197      counter_t *cntr;
198  
199      if(num_chan && names[0]) {
200          rtapi_print_msg(RTAPI_MSG_ERR,"num_chan= and names= are mutually exclusive\n");
201          return -EINVAL;
202      }
203      if(!num_chan && !names[0]) num_chan = default_num_chan;
204  
205      if(num_chan) {
206          howmany = num_chan;
207      } else {
208          howmany = 0;
209          for (i = 0; i < MAX_CHAN; i++) {
210              if ( (names[i] == NULL) || (*names[i] == 0) ){
211                  break;
212              }
213              howmany = i + 1;
214          }
215      }
216  
217      /* test for number of channels */
218      if ((howmany <= 0) || (howmany > MAX_CHAN)) {
219  	rtapi_print_msg(RTAPI_MSG_ERR,
220  	    "ENCODER: ERROR: invalid number of channels: %d\n", howmany);
221  	return -1;
222      }
223      /* have good config info, connect to the HAL */
224      comp_id = hal_init("encoder");
225      if (comp_id < 0) {
226  	rtapi_print_msg(RTAPI_MSG_ERR, "ENCODER: ERROR: hal_init() failed\n");
227  	return -1;
228      }
229      /* allocate shared memory for counter data */
230      counter_array = hal_malloc(howmany * sizeof(counter_t));
231      if (counter_array == 0) {
232  	rtapi_print_msg(RTAPI_MSG_ERR,
233  	    "ENCODER: ERROR: hal_malloc() failed\n");
234  	hal_exit(comp_id);
235  	return -1;
236      }
237      /* init master timestamp counter */
238      timebase = 0;
239      /* export all the variables for each counter */
240      i = 0; // for names= items
241      for (n = 0; n < howmany; n++) {
242  	/* point to struct */
243  	cntr = &(counter_array[n]);
244  	/* export all vars */
245          if(num_chan) {
246              char buf[HAL_NAME_LEN + 1];
247              rtapi_snprintf(buf, sizeof(buf), "encoder.%d", n);
248  	    retval = export_encoder(cntr,buf);
249          } else {
250  	    retval = export_encoder(cntr,names[i++]);
251          }
252  	if (retval != 0) {
253  	    rtapi_print_msg(RTAPI_MSG_ERR,
254  		"ENCODER: ERROR: counter %d var export failed\n", n);
255  	    hal_exit(comp_id);
256  	    return -1;
257  	}
258  	/* init counter */
259  	cntr->state = 0;
260  	cntr->oldZ = 0;
261  	cntr->Zmask = 0;
262  	*(cntr->x4_mode) = 1;
263  	*(cntr->counter_mode) = 0;
264  	*(cntr->latch_rising) = 1;
265  	*(cntr->latch_falling) = 1;
266  	cntr->buf[0].count_detected = 0;
267  	cntr->buf[1].count_detected = 0;
268  	cntr->buf[0].index_detected = 0;
269  	cntr->buf[1].index_detected = 0;
270  	cntr->bp = &(cntr->buf[0]);
271  	*(cntr->raw_counts) = 0;
272  	cntr->raw_count = 0;
273  	cntr->timestamp = 0;
274  	cntr->index_count = 0;
275  	cntr->latch_count = 0;
276  	*(cntr->count) = 0;
277  	*(cntr->min_speed) = 1.0;
278  	*(cntr->pos) = 0.0;
279  	*(cntr->pos_latch) = 0.0;
280  	*(cntr->vel) = 0.0;
281  	*(cntr->vel_rpm) = 0.0;
282  	*(cntr->pos_scale) = 1.0;
283  	cntr->old_scale = 1.0;
284  	cntr->scale = 1.0;
285  	cntr->counts_since_timeout = 0;
286      }
287      /* export functions */
288      retval = hal_export_funct("encoder.update-counters", update,
289  	counter_array, 0, 0, comp_id);
290      if (retval != 0) {
291  	rtapi_print_msg(RTAPI_MSG_ERR,
292  	    "ENCODER: ERROR: count funct export failed\n");
293  	hal_exit(comp_id);
294  	return -1;
295      }
296      retval = hal_export_funct("encoder.capture-position", capture,
297  	counter_array, 1, 0, comp_id);
298      if (retval != 0) {
299  	rtapi_print_msg(RTAPI_MSG_ERR,
300  	    "ENCODER: ERROR: capture funct export failed\n");
301  	hal_exit(comp_id);
302  	return -1;
303      }
304      rtapi_print_msg(RTAPI_MSG_INFO,
305  	"ENCODER: installed %d encoder counters\n", howmany);
306      hal_ready(comp_id);
307      return 0;
308  }
309  
310  void rtapi_app_exit(void)
311  {
312      hal_exit(comp_id);
313  }
314  
315  /***********************************************************************
316  *            REALTIME ENCODER COUNTING AND UPDATE FUNCTIONS            *
317  ************************************************************************/
318  
319  static void update(void *arg, long period)
320  {
321      counter_t *cntr;
322      atomic *buf;
323      int n;
324      unsigned char state;
325      int latch, old_latch, rising, falling;
326  
327      cntr = arg;
328      for (n = 0; n < howmany; n++) {
329  	buf = (atomic *) cntr->bp;
330  	/* get state machine current state */
331  	state = cntr->state;
332  	/* add input bits to state code */
333  	if (*(cntr->phaseA)) {
334  	    state |= SM_PHASE_A_MASK;
335  	}
336  	if (*(cntr->phaseB)) {
337  	    state |= SM_PHASE_B_MASK;
338  	}
339  	/* look up new state */
340  	if ( *(cntr->counter_mode) ) {
341  	    state = lut_ctr[state & (SM_LOOKUP_MASK & ~SM_PHASE_B_MASK)];
342  	} else if ( *(cntr->x4_mode) ) {
343  	    state = lut_x4[state & SM_LOOKUP_MASK];
344  	} else {
345  	    state = lut_x1[state & SM_LOOKUP_MASK];
346  	}
347  	/* should we count? */
348  	if (state & SM_CNT_UP_MASK) {
349  	    (*cntr->raw_counts)++;
350  	    buf->raw_count = *(cntr->raw_counts);
351  	    buf->timestamp = timebase;
352  	    buf->count_detected = 1;
353  	} else if (state & SM_CNT_DN_MASK) {
354  	    (*cntr->raw_counts)--;
355  	    buf->raw_count = *(cntr->raw_counts);
356  	    buf->timestamp = timebase;
357  	    buf->count_detected = 1;
358  	}
359  	/* save state machine state */
360  	cntr->state = state;
361  	/* get old phase Z state, make room for new bit value */
362  	state = cntr->oldZ << 1;
363  	/* add new value of phase Z */
364  	if (*(cntr->phaseZ)) {
365  	    state |= 1;
366  	}
367  	cntr->oldZ = state & 3;
368  	/* test for index enabled and rising edge on phase Z */
369  	if ((state & cntr->Zmask) == 1) {
370  	    /* capture counts, reset Zmask */
371  	    buf->index_count = *(cntr->raw_counts);
372  	    buf->index_detected = 1;
373  	    cntr->Zmask = 0;
374  	}
375          /* test for latch enabled and desired edge on latch-in */
376          latch = *(cntr->latch_in), old_latch = cntr->old_latch;
377          rising = latch && !old_latch;
378          falling = !latch && old_latch;
379  
380          if((rising && *(cntr->latch_rising))
381                  || (falling && *(cntr->latch_falling))) {
382              buf->latch_detected = 1;
383              buf->latch_count = *(cntr->raw_counts);
384          }
385          cntr->old_latch = latch;
386  
387  	/* move on to next channel */
388  	cntr++;
389      }
390      /* increment main timestamp counter */
391      timebase += period;
392      /* done */
393  }
394  
395  
396  static void capture(void *arg, long period)
397  {
398      counter_t *cntr;
399      atomic *buf;
400      int n;
401      rtapi_s32 delta_counts;
402      rtapi_u32 delta_time;
403      double vel, interp;
404  
405      cntr = arg;
406      for (n = 0; n < howmany; n++) {
407  	/* point to active buffer */
408  	buf = (atomic *) cntr->bp;
409  	/* tell update() to use the other buffer */
410  	if ( buf == &(cntr->buf[0]) ) {
411  	    cntr->bp = &(cntr->buf[1]);
412  	} else {
413  	    cntr->bp = &(cntr->buf[0]);
414  	}
415  	/* handle index */
416  	if ( buf->index_detected ) {
417  	    buf->index_detected = 0;
418  	    cntr->index_count = buf->index_count;
419  	    *(cntr->index_ena) = 0;
420  	}
421          /* handle latch */
422  	if ( buf->latch_detected ) {
423  	    buf->latch_detected = 0;
424  	    cntr->latch_count = buf->latch_count;
425  	}
426  
427  	/* update Zmask based on index_ena */
428  	if (*(cntr->index_ena)) {
429  	    cntr->Zmask = 3;
430  	} else {
431  	    cntr->Zmask = 0;
432  	}
433  	/* done interacting with update() */
434  	/* check for change in scale value */
435  	if ( *(cntr->pos_scale) != cntr->old_scale ) {
436  	    /* save new scale to detect future changes */
437  	    cntr->old_scale = *(cntr->pos_scale);
438  	    /* scale value has changed, test and update it */
439  	    if ((*(cntr->pos_scale) < 1e-20) && (*(cntr->pos_scale) > -1e-20)) {
440  		/* value too small, divide by zero is a bad thing */
441  		*(cntr->pos_scale) = 1.0;
442  	    }
443  	    /* we actually want the reciprocal */
444  	    cntr->scale = 1.0 / *(cntr->pos_scale);
445  	}
446          /* check for valid min_speed */
447          if ( *(cntr->min_speed) == 0 ) {
448              *(cntr->min_speed) = 1;
449          }
450  
451  	/* check reset input */
452  	if (*(cntr->reset)) {
453  	    /* reset is active, reset the counter */
454  	    /* note: we NEVER reset raw_counts, that is always a
455  		running count of edges seen since startup.  The
456  		public "count" is the difference between raw_count
457  		and index_count, so it will become zero. */
458  	    cntr->raw_count = *(cntr->raw_counts);
459  	    cntr->index_count = cntr->raw_count;
460  	}
461  	/* process data from update() */
462  	if ( buf->count_detected ) {
463  	    /* one or more counts in the last period */
464  	    buf->count_detected = 0;
465  	    delta_counts = buf->raw_count - cntr->raw_count;
466  	    delta_time = buf->timestamp - cntr->timestamp;
467  	    cntr->raw_count = buf->raw_count;
468  	    cntr->timestamp = buf->timestamp;
469  	    if ( cntr->counts_since_timeout < 2 ) {
470  		cntr->counts_since_timeout++;
471  	    } else {
472  		vel = (delta_counts * cntr->scale ) / (delta_time * 1e-9);
473  		*(cntr->vel) = vel;
474  	    }
475  	} else {
476  	    /* no count */
477  	    if ( cntr->counts_since_timeout ) {
478  		/* calc time since last count */
479  		delta_time = timebase - cntr->timestamp;
480  		if ( delta_time < 1e9 / ( *(cntr->min_speed) * cntr->scale )) {
481  		    /* not to long, estimate vel if a count arrived now */
482  		    vel = ( cntr->scale ) / (delta_time * 1e-9);
483  		    /* make vel positive, even if scale is negative */
484  		    if ( vel < 0.0 ) vel = -vel;
485  		    /* use lesser of estimate and previous value */
486  		    /* use sign of previous value, magnitude of estimate */
487  		    if ( vel < *(cntr->vel) ) {
488  			*(cntr->vel) = vel;
489  		    }
490  		    if ( -vel > *(cntr->vel) ) {
491  			*(cntr->vel) = -vel;
492  		    }
493  		} else {
494  		    /* its been a long time, stop estimating */
495  		    cntr->counts_since_timeout = 0;
496  		    *(cntr->vel) = 0;
497  		}
498  	    } else {
499  		/* we already stopped estimating */
500  		*(cntr->vel) = 0;
501  	    }
502  	}
503  	*(cntr->vel_rpm) = *(cntr->vel) * 60.0;
504  	/* compute net counts */
505  	*(cntr->count) = cntr->raw_count - cntr->index_count;
506          *(cntr->count_latch) = cntr->latch_count - cntr->index_count;
507  	/* scale count to make floating point position */
508  	*(cntr->pos) = *(cntr->count) * cntr->scale;
509  	*(cntr->pos_latch) = *(cntr->count_latch) * cntr->scale;
510  	/* add interpolation value */
511  	delta_time = timebase - cntr->timestamp;
512  	interp = *(cntr->vel) * (delta_time * 1e-9);
513  	*(cntr->pos_interp) = *(cntr->pos) + interp;
514  	/* move on to next channel */
515  	cntr++;
516      }
517      /* done */
518  }
519  
520  /***********************************************************************
521  *                   LOCAL FUNCTION DEFINITIONS                         *
522  ************************************************************************/
523  
524  static int export_encoder(counter_t * addr,char * prefix)
525  {
526      int retval, msg;
527  
528      /* This function exports a lot of stuff, which results in a lot of
529         logging if msg_level is at INFO or ALL. So we save the current value
530         of msg_level and restore it later.  If you actually need to log this
531         function's actions, change the second line below */
532      msg = rtapi_get_msg_level();
533      rtapi_set_msg_level(RTAPI_MSG_WARN);
534  
535      /* export pins for the quadrature inputs */
536      retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseA), comp_id,
537              "%s.phase-A", prefix);
538      if (retval != 0) {
539  	return retval;
540      }
541      retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseB), comp_id,
542              "%s.phase-B", prefix);
543      if (retval != 0) {
544  	return retval;
545      }
546      /* export pin for the index input */
547      retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseZ), comp_id,
548              "%s.phase-Z", prefix);
549      if (retval != 0) {
550  	return retval;
551      }
552      /* export pin for the index enable input */
553      retval = hal_pin_bit_newf(HAL_IO, &(addr->index_ena), comp_id,
554              "%s.index-enable", prefix);
555      if (retval != 0) {
556  	return retval;
557      }
558      /* export pin for the reset input */
559      retval = hal_pin_bit_newf(HAL_IN, &(addr->reset), comp_id,
560              "%s.reset", prefix);
561      if (retval != 0) {
562  	return retval;
563      }
564      /* export pins for position latching */
565      retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_in), comp_id,
566              "%s.latch-input", prefix);
567      if (retval != 0) {
568  	return retval;
569      }
570      retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_rising), comp_id,
571              "%s.latch-rising", prefix);
572      if (retval != 0) {
573  	return retval;
574      }
575      retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_falling), comp_id,
576              "%s.latch-falling", prefix);
577      if (retval != 0) {
578  	return retval;
579      }
580  
581      /* export parameter for raw counts */
582      retval = hal_pin_s32_newf(HAL_OUT, &(addr->raw_counts), comp_id,
583              "%s.rawcounts", prefix);
584      if (retval != 0) {
585  	return retval;
586      }
587      /* export pin for counts captured by capture() */
588      retval = hal_pin_s32_newf(HAL_OUT, &(addr->count), comp_id,
589              "%s.counts", prefix);
590      if (retval != 0) {
591  	return retval;
592      }
593      /* export pin for counts latched by capture() */
594      retval = hal_pin_s32_newf(HAL_OUT, &(addr->count_latch), comp_id,
595              "%s.counts-latched", prefix);
596      if (retval != 0) {
597  	return retval;
598      }
599      /* export pin for minimum speed estimated by capture() */
600      retval = hal_pin_float_newf(HAL_IN, &(addr->min_speed), comp_id,
601              "%s.min-speed-estimate", prefix);
602      if (retval != 0) {
603  	return retval;
604      }
605      /* export pin for scaled position captured by capture() */
606      retval = hal_pin_float_newf(HAL_OUT, &(addr->pos), comp_id,
607              "%s.position", prefix);
608      if (retval != 0) {
609  	return retval;
610      }
611      /* export pin for scaled and interpolated position captured by capture() */
612      retval = hal_pin_float_newf(HAL_OUT, &(addr->pos_interp), comp_id,
613              "%s.position-interpolated", prefix);
614      if (retval != 0) {
615  	return retval;
616      }
617      /* export pin for latched position captured by capture() */
618      retval = hal_pin_float_newf(HAL_OUT, &(addr->pos_latch), comp_id,
619              "%s.position-latched", prefix);
620      if (retval != 0) {
621  	return retval;
622      }
623      /* export pin for scaled velocity captured by capture() */
624      retval = hal_pin_float_newf(HAL_OUT, &(addr->vel), comp_id,
625              "%s.velocity", prefix);
626      if (retval != 0) {
627  	return retval;
628      }
629      /* export pin for scaled velocity captured by capture() */
630      retval = hal_pin_float_newf(HAL_OUT, &(addr->vel_rpm), comp_id,
631              "%s.velocity-rpm", prefix);
632      if (retval != 0) {
633  	return retval;
634      }
635      /* export pin for scaling */
636      retval = hal_pin_float_newf(HAL_IO, &(addr->pos_scale), comp_id,
637              "%s.position-scale", prefix);
638      if (retval != 0) {
639  	return retval;
640      }
641      /* export pin for x4 mode */
642      retval = hal_pin_bit_newf(HAL_IO, &(addr->x4_mode), comp_id,
643              "%s.x4-mode", prefix);
644      if (retval != 0) {
645  	return retval;
646      }
647      /* export pin for counter mode */
648      retval = hal_pin_bit_newf(HAL_IO, &(addr->counter_mode), comp_id,
649              "%s.counter-mode", prefix);
650      if (retval != 0) {
651  	return retval;
652      }
653      /* restore saved message level */
654      rtapi_set_msg_level(msg);
655      return 0;
656  }