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 }