Simple RTLinux Comedi Example, WAS: need simple example of comedi+rtlinux

Here is a simple example that I sent to you privately today but I am now
posting publicly to the mailing list so that the maybe others can see.

I am posting it inline to the message because I am not sure how this
mailing list handles attachments....

Cheers,

-Calin


Here goes:


------------------------------------------------------
./Makefile
------------------------------------------------------
#######################################################################
#
#  Makefile for rt_module.o
#
#
#######################################################################

# Makefile.dirs sets up variables that point to your
# rtl directory and your comedi direcories
include ./Makefile.dirs

include ${RTL_DIR}/rtl.mk

# These are the headers that go with comedi.  They are kernel headers for
# kcomedilib stuff and don't get installed in the usual /usr/include.
# Please point this define to your comedi source directory in the include/
# subdirectory.
COMEDI_DEVEL_HEADERS = ${COMEDI_DIR}/include

all: rt_module.o test

rt_module.o: rt_module.c rt_module.h
# Some checks are on the next line
	_at_( [ -d "${COMEDI_DEVEL_HEADERS}" ] && [ -r
"${COMEDI_DEVEL_HEADERS}/linux/comedilib.h" ] || ( \
	echo
"***************************************************************************";
\
	echo "ERROR: Cannot find the comedi kernel headers. "; \
	echo "       These are usually in your comedi source dirctory in
the include/ "; \
	echo "       subdirectory. Please set these in
Makefile.rt_process!"; \
	echo "       (COMEDI_DEVEL_HEADERS = /path/to/comedi/src/include)"
; \
	echo
"***************************************************************************";
\
	exit 1 ) )
	_at_echo
"***************************************************************************"
	_at_echo "              Compiling The RT-Module RealTime Kernel
Module"
	_at_echo
"***************************************************************************"
	$(CC) ${INCLUDE} ${CFLAGS} -I ${COMEDI_DEVEL_HEADERS} -c
rt_module.c

test.o: test.c rt_module.h
	$(CC) -I ${RTL_DIR}/include -c test.c

test: test.o
	$(CC) -o test test.o

clean:
	-rm -f test *.o *~
------------------------------------------------------
./test.c
------------------------------------------------------
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <mbuff.h>
#include "rt_module.h"

int main(void)
{
	struct SharedMem *shm = 0;
	int fd, n_read;
	const char *rtfname = "/dev/rtf" STR(DEFAULT_FIFO_MINOR);
	char fifo_buf[DEFAULT_FIFO_SIZE];

	shm = (struct SharedMem *)
		mbuff_attach(SHARED_MEM_NAME, sizeof(struct SharedMem));

	if (!shm || shm->magic != SHARED_MEM_MAGIC ) {
		fprintf(stderr, "Cannot attach to shared memory.. "
				"load rt_module.o!\n");
		return EINVAL;
	}

	fprintf(stderr, "Opening /dev/rtf%d...\n", DEFAULT_FIFO_MINOR);
	fflush(stderr);
	sleep(1); // for dramatic effect...

	fd = open(rtfname, O_RDONLY);

	if (fd < 0) {
		int saved_errno = errno;
		fprintf(stderr, "Error opening %s:%s", rtfname,
strerror(errno));
		exit(saved_errno);
	}

	fprintf(stderr, "Reading samples...");
	fflush(stderr);
	sleep(1); // for dramatic effect...

	while ( (n_read = read(fd, fifo_buf, DEFAULT_FIFO_SIZE)) >= 0) {
		int i, n_doubles = n_read / sizeof(double);

		for (i = 0; i < n_read; i++)
			printf("Read %g volts\n", ((double
*)fifo_buf)[n_doubles]);
	}
	return 0;
}
------------------------------------------------------
./rt_module.h
------------------------------------------------------
#ifndef _RT_PROCESS_H
# define _RT_PROCESS_H

#ifdef __cplusplus
extern "C" {
#endif

#define BILLION 1000000000
#define MILLION 1000000


#define DEFAULT_SAMPLING_RATE_HZ 1000

#define DEFAULT_COMEDI_MINOR   0  // /dev/comedi0
#define DEFAULT_COMEDI_CHANNEL 0  // just use the first channel
#define DEFAULT_CHANNEL_GAIN   0  // initial channel gain (COMEDI)
#define DEFAULT_FIFO_MINOR     0  // /dev/rtf0
#define DEFAULT_FIFO_SIZE      (1000 * sizeof(double))

#define STR_(x) #x
#define STR(x) "" STR_(x) ""

#ifdef __KERNEL__
#define const_
#else
#define const_ const
#endif

#define SHARED_MEM_MAGIC (0xdeadbeef)

struct SharedMem {
  const_ unsigned int ai_subdev;
  const_ double max_volts;
  const_ double min_volts;
  const_ unsigned long long scan_index;
  const_ int magic;
};

#define SHARED_MEM_NAME "Shared Memory Blah Blah"


#ifdef __cplusplus
}
#endif

#endif
------------------------------------------------------
./rt_module.c
------------------------------------------------------
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/bitops.h>
#include <asm/semaphore.h> /* for synchronizing the exported functions */
#include <asm/bitops.h>    /* for set/clear bit                        */
#include <linux/malloc.h>
#include <linux/string.h> /* some memory copyage */
#include <linux/fs.h> /* for the determine_minor() functionality */

#include <rtl.h>
#include <rtl_fifo.h>
#include <rtl_sched.h>
#include <rtl_sync.h>
#include <mbuff.h>

#include <linux/comedilib.h>

#include "rt_module.h"       /* header file specific to this program */


int init_module(void);        /* set up RT stuff */
void cleanup_module(void) ;    /* clean up RT stuff */
static int init_shared_mem(comedi_krange *); /* initialize shm */
static void *daq_rt_task (void *arg); /* our periodic loop */
static inline double sampl_to_volts(lsampl_t sampl);

#define KRANGE_MULTIPLIER (1e-6)
/* operates on krange_cache below to determine the voltage for a sample */
inline double sampl_to_volts(lsampl_t data);

static pthread_t daq_task = 0;          /* main RT task */

/* some vars to keep track of the rt task period/rate */
static hrtime_t task_period = 0;

/* the shared memory we will create and chare with userland */
static struct SharedMem *shm = 0;

/* Used in init_module so that subroutines can report errors... */
static int error;
static const char *errorMessage = "Success";

static lsampl_t maxdata = 0;         // from comedi_get_maxdata()
static unsigned int ai_subdev;
static int is_locked = 0, is_open = 0, have_rtf = 0;

/* this is our realtime periodic loop */
static void *daq_rt_task (void *arg)
{
  while (1) {
    lsampl_t data = 0;
    double ddata = 0;

    /* read 1 sample from the comedi device */
    comedi_data_read(DEFAULT_COMEDI_MINOR, shm->ai_subdev,
                     DEFAULT_COMEDI_CHANNEL, DEFAULT_CHANNEL_GAIN,
                     AREF_GROUND, &data);

    /* now put it in our realtime fifo, so user process can the sample */
    ddata = sampl_to_volts(data);
    rtf_put(DEFAULT_FIFO_MINOR, &ddata, sizeof(ddata));

    /* increment scan_index counter */
    ++shm->scan_index;

    /* wait for the next period (1 millisecond currently */
    pthread_wait_np();
  }

  return (void *)0;
}

int init_module(void)
{
  pthread_attr_t attr;
  struct sched_param sched_param;
  int tmp;
  comedi_krange krange;         // from comedi_krange struct


  error = -EBUSY;
  tmp =
    comedi_find_subdevice_by_type(DEFAULT_COMEDI_MINOR, COMEDI_SUBD_AI,
0);

  if ( tmp < 0 ) {
    error = tmp;
    errorMessage = "Cannot find an analog input subdevice on comedi device
"
      "minor number " STR(DEFAULT_COMEDI_MINOR) ".";
    goto init_error;
  }

  ai_subdev = tmp;

  tmp = comedi_lock(DEFAULT_COMEDI_MINOR, ai_subdev);

  if (tmp < 0) {
    error = tmp;
    errorMessage = "Cannot lock analog input subdevice.";
    goto init_error;
  }

  is_locked = 1;

  tmp = comedi_open(DEFAULT_COMEDI_MINOR);

  if (tmp < 0) {
    error = tmp;
    errorMessage = "Cannot open device with comedi minor number "
STR(DEFAULT_COMEDI_MINOR);
    goto init_error;
  }

  is_open = 1;

  maxdata = comedi_get_maxdata(DEFAULT_COMEDI_MINOR, ai_subdev,
                               DEFAULT_COMEDI_CHANNEL);

  comedi_get_krange(DEFAULT_COMEDI_MINOR, ai_subdev,
DEFAULT_COMEDI_CHANNEL,
                    DEFAULT_CHANNEL_GAIN, &krange);

  if ( init_shared_mem(&krange) )  {
    /* error initializing shared memory */
    errorMessage = "Cannot allocate mbuff shared memory.  Did you create
the "
                   "device node? Are you low on memory?";
    error = -ENOMEM;
    goto init_error;
  }

  tmp = rtf_create(DEFAULT_FIFO_MINOR, DEFAULT_FIFO_SIZE);

  if (tmp != DEFAULT_FIFO_SIZE && tmp != 0) {
    error = -EBUSY;
    errorMessage = "Couldn't create fifo at minor "
STR(DEFAULT_FIFO_MINOR) ". ";
    goto init_error;
  }

  have_rtf = 1;

  /* create the realtime task */
  pthread_attr_init(&attr);
  pthread_attr_setfp_np(&attr, 1);
  sched_param.sched_priority = 1;
  pthread_attr_setschedparam(&attr, &sched_param);
  if ( (error = pthread_create(&daq_task, &attr, daq_rt_task, (void *)0) )
) {
    errorMessage = "Cannot create the daq pthread";
    goto init_error;
  }

  task_period = ((1000 / DEFAULT_SAMPLING_RATE_HZ ) * MILLION);

  /* now re-tune the period, note that this is dangerous if set too
     fast, as the user task may never get to run! */
  error = pthread_make_periodic_np (daq_task, gethrtime() + task_period,
                                    task_period);

  if ( error ) {
    errorMessage = "Cannot make DAQ RT Task periodic";
    goto init_error;
  }

  printk("rt_module : acquisition started at %d Hz (%s)\n",
         DEFAULT_SAMPLING_RATE_HZ, errorMessage);
  return 0;

 init_error:
  cleanup_module();
  printk("rt_module: Failure -- can't initalize RT task (%s)\n",
errorMessage);
  return error;
}

/*  Initializes the rtlinux shared memory returns 0 on success, 1 on
failure */
static int
init_shared_mem(comedi_krange * krange)
{
  /* Open mbuff Shared Memory structure */
  shm =
    (struct SharedMem *) mbuff_alloc (SHARED_MEM_NAME,
                                      sizeof(struct SharedMem));

  if (! shm) { /* means there was some sort of error allocating mbuff */
    return -ENOMEM;
  }

  /* initialize shared memory variables ... see rt_module.h for
definitions */
  shm->magic = SHARED_MEM_MAGIC;
  shm->ai_subdev = ai_subdev;
  shm->scan_index = 0;

  shm->max_volts = krange->max * (double)KRANGE_MULTIPLIER;
  shm->min_volts = krange->min * (double)KRANGE_MULTIPLIER;

  if (RF_UNIT(krange->flags) == UNIT_mA) {
    shm->max_volts *= 0.001;
    shm->min_volts *= 0.001;
  }

  return 0;
}

void cleanup_module(void)
{
  /* delete daq_task */
  if (daq_task) {
    if (!pthread_delete_np(daq_task))
      printk ("rt_module: deleted RT task after %lu cycles.\n",
	      (unsigned long int)(shm ? shm->scan_index + 1 : 0));
    else
      printk ("rt_module: cannot find RT task (it died?).\n");
  }

  /* close all successfully opened fifos */
  if (have_rtf) rtf_destroy(DEFAULT_FIFO_MINOR);

  if (is_locked) {
    comedi_cancel(DEFAULT_COMEDI_MINOR, ai_subdev);
    comedi_unlock(DEFAULT_COMEDI_MINOR, ai_subdev);
  }

  if (is_open)  comedi_close(DEFAULT_COMEDI_MINOR);

  if (shm)  mbuff_free(SHARED_MEM_NAME, shm);

}

static inline double sampl_to_volts(lsampl_t sampl)
{
  double ret;

  ret =
    ((shm->max_volts - shm->min_volts) * (sampl/(double)maxdata)
     + shm->min_volts) * KRANGE_MULTIPLIER;

  return ret;
}


------------------------------------------------------
./Makefile.dirs
------------------------------------------------------
RTL_DIR = /usr/src/rtlinux
COMEDI_DIR = /usr/src/comedi


On Thu, 23 May 2002, Igal Brener wrote:

> List,
>
>    Can anyone send me a VERY simple example of an rtlinux program that uses
> comedi, say for just one simple ADC call?
>
> I'm using Rtlinux 3.0, Comedi 0.7.64 and Comedilib 0.7.18.
>
> Thanks a lot.
>
> Igal
>
>
> _______________________________________________
> comedi mailing list
> comedi_at_stm.lbl.gov
> http://stm.lbl.gov/cgi-bin/mailman/listinfo/comedi
>

Received on 2002-05-24Z20:58:28