Re: NI PCI-6221 encoder (Was: JR3 force sensor driver)

Anders Blomdell wrote:
> Anders Blomdell wrote:
>> Anders Blomdell wrote:
>>> Anders Blomdell wrote:
>>>> Frank Mori Hess wrote:
>>>>> On Thursday 21 June 2007 13:36, you wrote:
>>>>>> I know (I'll get to it as soon as I have figured out how to use the NI
>>>>>> pci-6221 with quadrature encoders).
>>>>> See the enums in comedi.h, in particular enum ni_gpct_mode_bits.  You'll 
>>>>> need to use one of the NI_GPCT_COUNTING_MODE_QUADRATURE_XN_BITS bitfields, 
>>>>> instead of the NI_GPCT_COUNTING_MODE_NORMAL_BITS that the simple counting 
>>>>> demo uses.  And maybe some of the NI_GPCT_INDEX_* bits.  It might actually 
>>>>> be useful to look at the ni 6601/6602 register level programming manual.
>>>> That only partially works, what happens is that whatever
>>>> NI_GPCT_COUNTING_MODE_QUADRATURE_XN_BITS is selected, the counter starts to
>>>> count down on each edge on CTR{0/1}A (PFI 8/PFI 3) but remains stationary
>>>> regardless of activity on CTR{0/1}B (PFI 10/PFI 11). To me this indicates that
>>>> the counter is not in encoder mode, because a failure in the routing of the B
>>>> signal should result in the counter changing one value only (X0 -> X1 -> X0
>>>> transitions should gige one up and one down event). Direction is changed if
>>>> NI_GPCT_COUNTING_DIRECTION_UP_BITS is set.
>>>>
>>>> Where can I find the documentation where M_Offset_G0_Counting_Mode, etc are
>>>> found (the only document I have found on NI's site is "NI M Series Register Map
>>>> (Preliminay)"
>>> The following hack (gleaned after getting a working example in the NI
>>> nimhddk_linux26/nimseries register level programming), makes it work:
>>>
>>>   case NITIO_G0_Counting_Mode_Reg:
>>>     ni_writew(bits, M_Offset_G0_Counting_Mode);
>>>     bits = 0x26a0;
>>>     ni_writew(bits, M_Offset_G0_MSeries_ABZ);
>>>     break;
>>>
>>> Seems like we (I?) need to add functionality to do the equivalent of (see
>>> attached NI/RLP example for context):
>>>
>>>   board->G0_MSeries_ABZ.setG0_A_Select(9);
>>>   board->G0_MSeries_ABZ.setG0_B_Select(21);
>>>   board->G0_MSeries_ABZ.flush();
>>>
>>> any comments on what it should look like (or is it already there, but hidden by
>>> my ignorance)?
>> I have looked a little more on the comedi code. The functionality seems to be
>> lacking (or I'm totally blind). Which of the following implementations is preferred:
>>
>> 1. Adding INSN_CONFIG_SET_ABZ_SRC, which accepts two arguments:
>>      index [0=A, 1=B, 2=Z]
>>      source [1-10=PFI0-PFI9, 21-26=PFI10-PFI15, 31=DISABLED, ...]
>>
>> 2. Adding INSN_CONFIG_SET_A_SRC, INSN_CONFIG_SET_B_SRC, INSN_CONFIG_SET_Z_SRC,
>> which accepts 1 argument:
>>     source [1-10=PFI0-PFI9, 21-26=PFI10-PFI15, 31=DISABLED, ...]
>>
>> 3. Modify INSN_CONFIG_SET_GATE_SRC to accept
>>      index [2=A, 3=B, 4=Z]
>>      source [1-10=PFI0-PFI9, 21-26=PFI10-PFI15, 31=DISABLED, ...]
>>
>> 4. None of the above, please fill in a rough sketch on dotted line below:
>>
>>       ...........................................
> Selected #3 above, patches to comedi & comedilib are included. Have not got Z
> signal to work, but at least A/B works (will get back if I find what is missing)...
OK, with slightly modified demo/gpct_encoder.c I got Z to work as well

Regards

Anders Blomdell
-- 
Anders Blomdell                  Email: anders.blomdell_at_control.lth.se
Department of Automatic Control
Lund University                  Phone:    +46 46 222 4625
P.O. Box 118                     Fax:      +46 46 138118
SE-221 00 Lund, Sweden
/*
 * NI quadrature encoder example.
 * Part of Comedilib
 *
 * Copyright (c) 2007 Anders Blomdell <anders.blomdell_at_control.lth.se>
 *
 * This file may be freely modified, distributed, and combined with
 * other software, as long as proper attribution is given in the
 * source code.
 */
/*
 * Requirements:  A board with a National Instruments general-purpose
 * counter, and comedi driver version 0.7.74 or newer.
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <comedilib.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <ctype.h>
#include "examples.h"

int ni_gpct_start_encoder(comedi_t *device, unsigned subdevice, 
			  unsigned int initial_value,
			  int a, int b, int z)
{
	int retval;
	lsampl_t counter_mode;
	

	retval = reset_counter(device, subdevice);
        /* set initial counter value by writing to channel 0 */
        retval = comedi_data_write(device, subdevice, 0, 0, 0, initial_value);
        /* set "load a" register to initial_value by writing to channel 1 */
        retval = comedi_data_write(device, subdevice, 1, 0, 0, initial_value);
        /* set "load b" register to initial_value by writing to channel 2 */
        retval = comedi_data_write(device, subdevice, 2, 0, 0, initial_value);

	set_gate_source(device, subdevice, 0, NI_GPCT_DISABLED_GATE_SELECT);
	set_gate_source(device, subdevice, 1, NI_GPCT_DISABLED_GATE_SELECT);
	set_gate_source(device, subdevice, 2, a);
	set_gate_source(device, subdevice, 3, b);
	set_gate_source(device, subdevice, 4, z);

	counter_mode = (NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS |
			NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS);
	if (z != NI_GPCT_DISABLED_GATE_SELECT) {
	  counter_mode |= NI_GPCT_INDEX_ENABLE_BIT;
	}
	retval = set_counter_mode(device, subdevice, counter_mode);
	if(retval < 0) return retval;

	retval = arm(device, subdevice, NI_GPCT_ARM_IMMEDIATE);
	if(retval < 0) return retval;

	return 0;
}

int main(int argc, char *argv[])
{
  comedi_t *device = NULL;
  int subdevice = -1;
  int a = NI_GPCT_DISABLED_GATE_SELECT;
  int b = NI_GPCT_DISABLED_GATE_SELECT;
  int z = NI_GPCT_DISABLED_GATE_SELECT;
  unsigned int initial_value = 0;
  int retval;

  struct parsed_options options;
  {
    int c;
    while (-1 != (c = getopt(argc, argv, "f:s:a:b:z:i:"))) {
      switch (c) {
	case 'f':
	  device = comedi_open(optarg);
	  if(!device) {
	    comedi_perror(optarg);
	    exit(-1);
	  }
	  break;
	case 's':
	  subdevice = strtoul(optarg, NULL, 0);
	  break;
	case 'a':
	  /* TODO: Should we pass the value directly, i.e. could anybody
	   *       be interested in values besides PFIx/DISABLED */
	  a = NI_GPCT_PFI_GATE_SELECT(strtoul(optarg, NULL, 0));
	  break;
	case 'b':
	  /* TODO: Should we pass the value directly, i.e. could anybody
	   *       be interested in values besides PFIx/DISABLED */
	  b = NI_GPCT_PFI_GATE_SELECT(strtoul(optarg, NULL, 0));
	  break;
	case 'z':
	  /* TODO: Should we pass the value directly, i.e. could anybody
	   *       be interested in values besides PFIx/DISABLED */
	  z = NI_GPCT_PFI_GATE_SELECT(strtoul(optarg, NULL, 0));
	  break;
	case 'i':
	  initial_value = strtoul(optarg, NULL, 0);
      break;
      }
    }
  }

  /*FIXME: check that device is counter */
  printf("Initiating encoder on subdevice %d.\n", subdevice);
  
  retval = ni_gpct_start_encoder(device, subdevice, initial_value, a, b, z);
  
  if(retval < 0) return retval;
  
  return 0;
}

Received on 2007-06-26Z15:02:25