RE: Simulink & COMEDI_DIO_READ issues

Well, this *is* a question for the Comedi list. Lovely, isn't it, how these subjects mingle? :)
 
As I understand it, there are basic guidelines written a couple of years ago by the Comedi maintainers for encoder device drivers. But basically, only a few encoder devices are supported by Comedi --though I can't name any besides the NI-660x series-- and I have no idea which are the other encoder devices, nor how they are configured. 
 
I was very glad Klaas Gadeyne and collegues had taken the time and effort to write the driver for the NI-660x for I had that card already. Then I needed a way to configure the thing in Simulink, and with the help of Roberto Bucher and Klaas' test code, wrote the Simulink block. It, perhaps, is a bit of a hack, but it works.
 
But as I said above, without knowing anything about other encoder device drivers, nor how they are implemented, I can't give any guarantees if the Simulink encoder block will work for anything else then a NI-660x encoder.
 
But perhaps Klaas or someone else can further clarify the issue? It is a bit misty to me too...
 
Kind regards,
Arno.

________________________________

From: janne [mailto:caminman_at_gmail.com]
Sent: Fri 07/04/2006 11:12
To: Stienen, A.H.A. (CTW)
Cc: RTAI
Subject: Re: Simulink & COMEDI_DIO_READ issues


Hello again Arno,
 
Thank you very much for this. The block works nicely!
 
Since I'm new to all this (Control Systems, RTAI, Comedi, Simulink..), 
I have plenty of different fields to explore I unfortunately do not have much 
time dwelling into details; so just out of curiousity, I saw your comment about
this block only working with NI 660x and devices configured for encoding in
the same manner. So the question is, is there a lack of an abstraction layer
in the Comedi library when it comes to encoders?  That there isn't a general
way of setting up your card as an encoder (or even counter?)  ?. I assume that
if this was the case, you would not have the block you created is only for
NI 660x.
   And if it is like this, is there any plans of changing (adding) this?
 
regards,
Camino
 
 
On 4/5/06, Stienen, A.H.A. (CTW) <a.h.a.stienen_at_utwente.nl> wrote: 

	(moved here from the Comedi mailing list)
	
	Hi Camino,
	
	What you're trying to do, is impossible. Luckely for you, I've adapted 
	one of Roberto Bucher's Comedi Simulink blocks with code from the NI
	660x test programs of Klaas Gadeyne, to be able to use my NI 6602.
	
	janne wrote:
	> I want to use the COMEDI_DIO_READ block in Simulink to read the encoder 
	> values from a NI 6602.
	> [...]
	> If I run the model without any specific configuration, I get an error
	> saying that Comedi lock subdevice fails (error -38).
	
	Logically, because the function you are looking for is DIO subdevice, 
	whereas what you really want is a COUNTER subdevice.
	
	I made two changes to the Simulink devices source-files which came with
	RTAI 3.2. I'm assuming the code of these files do not change that much
	for each version of RTAI. (Is this correct, Roberto?) 
	
	First of all, I copied the code from sfun_comedi_data_read.c to
	sfun_comedi_encoder.c and modified it. The new file is attached here and
	should be placed (and MEX-ed) in your 'rtw/c/rtai/devices'  directory. 
	
	Secondly, to be able to see the new encoder block in Simulink, you'll
	need to add a couple of lines to rtai_devices.mdl. This are these lines:
	
	    Block {
	      BlockType                      "S-Function" 
	      Name                   "Comedi Encoder"
	      Ports                  [0, 1]
	      Position               [20, 417, 140, 463]
	      BackgroundColor        "[0.905882, 0.901961, 0.976471]" 
	      FontSize               10
	      FunctionName           "sfun_comedi_encoder"
	      Parameters             "dev,chan,quadmode,ireload,iphase,dt"
	      MaskType               "Comedi Encoder" 
	      MaskDescription        "See Comedi documentation."
	      MaskPromptString       "Device:|Channel:|Quadrature mode:|Reload
	at index pulse:|Index phase:|Sample time [s]:"
	      MaskStyleString 
	"popup(comedi0|comedi1|comedi2|comedi3),edit,popup(X1|X2|X4),popup(no|yes),popup(A
	high B high|A low B high|A low B low|A high B low),edit"
	      MaskTunableValueString  "off,off,off,off,off,off" 
	      MaskCallbackString      "|||||"
	      MaskEnableString       "on,on,on,on,on,on"
	      MaskVisibilityString    "on,on,on,on,on,on"
	      MaskToolTipString              "on,on,on,on,on,on" 
	      MaskVarAliasString      ",,,,,"
	      MaskVariables
	"dev=_at_1;chan=_at_2;quadmode=_at_3;ireload=_at_4;iphase=_at_5;dt=_at_6;"
	      MaskDisplay            "disp('Comedi
	Encoder')\nport_label('output',1,int2str(chan))" 
	      MaskIconFrame          on
	      MaskIconOpaque         on
	      MaskIconRotate         "none"
	      MaskIconUnits          "autoscale"
	      MaskValueString        "comedi0|0|X4|no|A high B high|0.001" 
	      MaskTabNameString              ",,,,,"
	    }
	
	Now you'll be able to configure your encoders from Simulink!
	
	Hope this works,
	Arno Stienen.
	
	--
	  ____
	/  |_| <<< IR. ARNO H.A. STIENEN >>>
	|_ O  | Biomechanical Engineering | http://www.bw.ctw.utwente.nl
	|_|__/  a.h.a.stienen_at_utwente.nl  | +31-(0)53-489-4778 
	UTwente "There are alternatives: Firefox, Thunderbird & OpenOffice!"
	
	
	
	
	/*
	COPYRIGHT (C) 2003  Lorenzo Dozio (dozio_at_aero.polimi.it)
	
	This library is free software; you can redistribute it and/or 
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2 of the License, or (at your option) any later version.
	
	This library is distributed in the hope that it will be useful, 
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public 
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
	*/
	
	#define S_FUNCTION_NAME         sfun_comedi_encoder
	#define S_FUNCTION_LEVEL        2 
	
	#ifdef MATLAB_MEX_FILE
	#include "mex.h"
	#endif
	#include "simstruc.h"
	
	#define NUMBER_OF_PARAMS                (6)
	
	#define COMEDI_DEVICE_PARAM             ssGetSFcnParam(S,0) 
	#define COMEDI_CHANNEL_PARAM    ssGetSFcnParam(S,1)
	#define COMEDI_QUADMODE_PARAM   ssGetSFcnParam(S,2)
	#define COMEDI_IRELOAD_PARAM    ssGetSFcnParam(S,3)
	#define COMEDI_IPHASE_PARAM             ssGetSFcnParam(S,4) 
	#define SAMPLE_TIME_PARAM               ssGetSFcnParam(S,5)
	
	#define COMEDI_DEVICE                   ((uint_T) mxGetPr(COMEDI_DEVICE_PARAM)[0])
	#define COMEDI_CHANNEL                  ((uint_T) mxGetPr(COMEDI_CHANNEL_PARAM)[0]) 
	#define COMEDI_QUADMODE                 ((uint_T) mxGetPr(COMEDI_QUADMODE_PARAM)[0])
	#define COMEDI_IRELOAD                  ((uint_T) mxGetPr(COMEDI_IRELOAD_PARAM)[0])
	#define COMEDI_IPHASE                   ((uint_T) mxGetPr(COMEDI_IPHASE_PARAM)[0]) 
	#define SAMPLE_TIME                             ((real_T) mxGetPr(SAMPLE_TIME_PARAM)[0])
	
	#define ENCODER_CONFIG_DATA     (4)
	
	#ifndef MATLAB_MEX_FILE
	
	#include <stdio.h>
	#include <unistd.h >
	#include <sys/types.h>
	#include <sys/mman.h>
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <math.h>
	
	#include <rtai_lxrt.h>
	#include <rtai_comedi.h> 
	
	extern void *ComediDev[];
	extern int ComediDev_InUse[];
	extern int ComediDev_AIInUse[];
	
	#endif
	
	#define MDL_CHECK_PARAMETERS
	#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE) 
	static void mdlCheckParameters(SimStruct *S)
	{
	       static char_T errMsg[256];
	       boolean_T allParamsOK = 1;
	
	       if (mxGetNumberOfElements(COMEDI_CHANNEL_PARAM) != 1) {
	               sprintf(errMsg, "Channel parameter must be a scalar.\n"); 
	               allParamsOK = 0;
	               goto EXIT_POINT;
	       }
	
	EXIT_POINT:
	       if (!allParamsOK) {
	               ssSetErrorStatus(S, errMsg);
	       }
	}
	#endif
	
	static void mdlInitializeSizes(SimStruct *S) 
	{
	       uint_T i;
	
	       ssSetNumSFcnParams(S, NUMBER_OF_PARAMS);
	#if defined(MATLAB_MEX_FILE)
	       if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
	               mdlCheckParameters(S);
	               if (ssGetErrorStatus(S) != NULL) {
	                       return;
	               }
	       } else {
	               return;
	       }
	#endif
	       for (i = 0; i < NUMBER_OF_PARAMS; i++) { 
	               ssSetSFcnParamNotTunable(S, i);
	       }
	       ssSetNumInputPorts(S, 0);
	       ssSetNumOutputPorts(S, 1);
	       ssSetOutputPortWidth(S, 0, 1);
	       ssSetNumContStates(S, 0);
	       ssSetNumDiscStates(S, 0); 
	       ssSetNumSampleTimes(S, 1);
	       ssSetNumPWork(S,1); // Specify the size of a block's pointer work vector.
	       ssSetNumIWork(S,1); // Specify the size of a block's integer work vector.
	       ssSetNumRWork(S,0); // Specify the size of a block's floating-point work vector. 
	}
	
	static void mdlInitializeSampleTimes(SimStruct *S)
	{
	       ssSetSampleTime(S, 0, SAMPLE_TIME);
	       ssSetOffsetTime(S, 0, 0.0);
	}
	
	#define MDL_START
	#if defined(MDL_START)
	static void mdlStart(SimStruct *S) 
	{
	#ifndef MATLAB_MEX_FILE
	       void *dev;
	       int subdev;
	       int index                               = (int)COMEDI_DEVICE - 1;
	       unsigned int channel    = (unsigned int)COMEDI_CHANNEL;
	       unsigned int quadmode   = (unsigned int)COMEDI_QUADMODE - 1;
	       unsigned int ireload    = (unsigned int)COMEDI_IRELOAD - 1;
	       unsigned int iphase             = (unsigned int)COMEDI_IPHASE - 1;
	       char *devname[4]                = {"/dev/comedi0","/dev/comedi1","/dev/comedi2","/dev/comedi3"}; 
	       int quadmodeint[3]              = {GPCT_X1,GPCT_X2,GPCT_X4};
	       int ireloadint[2]               = {0,GPCT_RESET_COUNTER_ON_INDEX};
	       int iphaseint[4]                = {GPCT_IndexPhaseHighHigh,GPCT_IndexPhaseLowHigh,GPCT_IndexPhaseLowLow,GPCT_IndexPhaseHighLow}; 
	       int n_channels;
	       int ret;
	       char board[50];
	       static char_T errMsg[256];
	       comedi_insn insn;
	       lsampl_t config_data[ENCODER_CONFIG_DATA];
	
	   if (!ComediDev[index]) { 
	               dev = comedi_open(devname[index]);
	               if (!dev) {
	                       sprintf(errMsg, "Comedi open failed\n");
	                       ssSetErrorStatus(S, errMsg);
	                       printf("%s", errMsg); 
	                       return;
	               }
	               rt_comedi_get_board_name(dev, board);
	               printf("\nCOMEDI %s (%s) opened.\n", devname[index], board);
	               ComediDev[index] = dev; 
	       if ((subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_COUNTER, 0)) < 0) {
	                       sprintf(errMsg, "Comedi find_subdevice failed (No counter)\n");
	                       ssSetErrorStatus(S, errMsg); 
	                       printf("%s", errMsg);
	                       comedi_close(dev);
	                       return;
	               }
	               if ((comedi_lock(dev, subdev)) < 0) {
	                       sprintf(errMsg, "Comedi lock failed for subdevice %d\n", subdev); 
	                       ssSetErrorStatus(S, errMsg);
	                       printf("%s", errMsg);
	                       comedi_close(dev);
	                       return;
	               }
	       } else { 
	               dev = ComediDev[index];
	               subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_COUNTER, 0);
	       }
	       if ((n_channels = comedi_get_n_channels(dev, subdev)) < 0) {
	               sprintf(errMsg, "Comedi get_n_channels failed for subdevice %d\n", subdev); 
	               ssSetErrorStatus(S, errMsg);
	               printf("%s", errMsg);
	               comedi_unlock(dev, subdev);
	               comedi_close(dev);
	               return;
	       }
	       if (channel >= n_channels) {
	               sprintf(errMsg, "Comedi channel not available for subdevice %d\n", subdev);
	               ssSetErrorStatus(S, errMsg);
	               printf("%s", errMsg); 
	               comedi_unlock(dev, subdev);
	               comedi_close(dev);
	               return;
	       }
	       insn.insn                       = INSN_CONFIG;
	       insn.n                          = 1; 
	       config_data[0]          = INSN_CONFIG_GPCT_QUADRATURE_ENCODER;
	       config_data[1]          = quadmodeint[quadmode];
	       config_data[2]          = iphaseint[iphase];
	       config_data[3]          = ireloadint[ireload]; 
	       insn.data                       = config_data;
	       insn.subdev                     = subdev;
	       insn.chanspec           = CR_PACK(channel,0,0);
	       ret                 = comedi_do_insn(dev,&(insn)); 
	       if (ret < 0) {
	               sprintf(errMsg, "Comedi encoder config failed for subdevice %d\n", subdev);
	               ssSetErrorStatus(S, errMsg);
	               printf("%s", errMsg); 
	               comedi_unlock(dev, subdev);
	               comedi_close(dev);
	               return;
	       }
	       ComediDev_InUse[index]++;
	       ComediDev_AIInUse[index]++;
	       printf("Encoder Channel %d - Index Pulse Reset : %d\n", channel, ireload); 
	       ssGetPWork(S)[0] = (void *)dev;
	       ssGetIWork(S)[0] = subdev;
	#endif
	}
	#endif
	
	static void mdlOutputs(SimStruct *S, int_T tid)
	{
	       double *y = ssGetOutputPortRealSignal(S,0);
	
	#ifndef MATLAB_MEX_FILE
	       unsigned int channel    = (unsigned int)COMEDI_CHANNEL;
	       void *dev                       = (void *)ssGetPWork(S)[0];
	       int subdev                      = ssGetIWork(S)[0]; 
	       //lsampl_t maxdata              = comedi_get_maxdata(dev, subdev, channel);
	       lsampl_t data;
	       int x;
	
	       comedi_data_read(dev, subdev, channel, 0, 0, &data);
	       x = (int) data; // Move higher half of lsampl_t (uint32) data to negetive values. 
	       *y = (double) x;
	#endif
	}
	
	static void mdlTerminate(SimStruct *S)
	{
	#ifndef MATLAB_MEX_FILE
	       int index  = (int)COMEDI_DEVICE - 1;
	       void *dev  = (void *)ssGetPWork(S)[0];
	       int subdev = ssGetIWork(S)[0]; 
	       char *devname[4] = {"/dev/comedi0","/dev/comedi1","/dev/comedi2","/dev/comedi3"};
	
	       if (ssGetErrorStatus(S) == NULL) {
	               ComediDev_InUse[index]--; 
	               ComediDev_AIInUse[index]--;
	               if (!ComediDev_AIInUse[index]) {
	                       comedi_unlock(dev, subdev);
	               }
	               if (!ComediDev_InUse[index]) {
	                       comedi_close(dev);
	                       printf("\nCOMEDI %s closed.\n", devname[index]);
	                       ComediDev[index] = NULL;
	               }
	       }
	#endif
	}
	
	#ifdef  MATLAB_MEX_FILE
	#include "simulink.c"
	#else
	#include "cg_sfun.h"
	#endif
	
	
	

Received on 2006-04-07Z10:07:10