Added new driver from sutashu@gmail.com (Stanislaw Raczynski)
authorFrank Mori Hess <fmhess@speakeasy.net>
Tue, 2 May 2006 02:01:09 +0000 (02:01 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Tue, 2 May 2006 02:01:09 +0000 (02:01 +0000)
comedi/drivers/Kbuild
comedi/drivers/Makefile.am
comedi/drivers/mpc624.c [new file with mode: 0644]

index 3628a3ca1adbf9ffbbe0c31f87e41959773b7695..e3f25dcfa5b13c8ed2e01fefaa307d78ff2f4f3b 100644 (file)
@@ -65,6 +65,7 @@ obj-m += icp_multi.o
 obj-m += ke_counter.o
 obj-m += me_daq.o
 obj-m += me4000.o
+obj-m += mpc624.o
 obj-m += multiq3.o
 obj-m += ni_660x.o
 obj-m += ni_670x.o
index b7d651ac904a4394d327e14b0f0fee28cd4959cd..82909dbdcb0f90ae049a5fa9ff25a65da227d91c 100644 (file)
@@ -133,6 +133,7 @@ module_PROGRAMS = \
  ke_counter.ko \
  me_daq.ko \
  me4000.ko \
+ mpc624.ko \
  multiq3.ko \
  ni_660x.ko \
  ni_670x.ko \
@@ -220,6 +221,7 @@ ke_counter_ko_SOURCES = ke_counter.c
 me4000_ko_SOURCES = me4000.c
 me_daq_ko_SOURCES = me_daq.c
 mite_ko_SOURCES = mite.c
+mpc624_ko_SOURCES = mpc624.c
 multiq3_ko_SOURCES = multiq3.c
 ni_660x_ko_SOURCES = ni_660x.c
 ni_670x_ko_SOURCES = ni_670x.c
diff --git a/comedi/drivers/mpc624.c b/comedi/drivers/mpc624.c
new file mode 100644 (file)
index 0000000..b67e2da
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+    comedi/drivers/mpc624.c
+    Hardware driver for a Micro/sys inc. MPC-624 PC/104 board
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: mpc624.o
+Description: Micro/sys MPC-624 PC/104 board
+Devices: [Micro/sys] MPC-624 (mpc624)
+Author: Stanislaw Raczynski <sraczynski@op.pl>
+Updated: Thu, 15 Sep 2005 12:01:18 +0200
+Status: working
+
+    The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
+    ADC chip.
+    
+    Subdevices supported by the driver:
+    - Analog In:   supported
+    - Digital I/O: not supported
+    - LEDs:        not supported
+    - EEPROM:      not supported
+
+Configuration Options:
+  [0] - I/O base address
+  [1] - convertion rate
+            Convertion rate  RMS noise  Effective Number Of Bits
+         0      3.52kHz        23uV                17
+         1      1.76kHz       3.5uV                20
+         2       880Hz         2uV                21.3
+         3       440Hz        1.4uV               21.8
+         4       220Hz         1uV                22.4
+         5       110Hz        750uV               22.9
+         6       55Hz         510nV               23.4
+         7      27.5Hz        375nV                24 
+         8      13.75Hz       250nV               24.4
+         9      6.875Hz       200nV               24.6
+   [2] - voltage range
+         0      -1.01V .. +1.01V
+         1      -10.1V .. +10.1V
+*/
+
+#include <linux/comedidev.h>
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+// Consecutive I/O port addresses
+#define MPC624_SIZE             16     
+
+// Offsets of different ports
+#define MPC624_MASTER_CONTROL  0    // not used
+#define MPC624_GNMUXCH          1    // Gain, Mux, Channel of ADC
+#define MPC624_ADC              2    // read/write to/from ADC
+#define MPC624_EE               3    // read/write to/from serial EEPROM via I2C
+#define MPC624_LEDS             4    // write to LEDs
+#define MPC624_DIO              5    // read/write to/from digital I/O ports
+#define MPC624_IRQ_MASK         6    // IRQ masking enable/disable
+
+// Register bits' names
+#define MPC624_ADBUSY           (1<<5)
+#define MPC624_ADSDO            (1<<4)
+#define MPC624_ADFO             (1<<3)
+#define MPC624_ADCS             (1<<2)
+#define MPC624_ADSCK            (1<<1)
+#define MPC624_ADSDI            (1<<0)
+
+// SDI Speed/Resolution Programming bits
+#define MPC624_OSR4             (1<<31)
+#define MPC624_OSR3             (1<<30)
+#define MPC624_OSR2             (1<<29)
+#define MPC624_OSR1             (1<<28)
+#define MPC624_OSR0             (1<<27)
+
+// 32-bit output value bits' names
+#define MPC624_EOC_BIT          (1<<31)
+#define MPC624_DMY_BIT          (1<<30)
+#define MPC624_SGN_BIT          (1<<29)
+
+// Convertion speeds
+/* OSR4 OSR3 OSR2 OSR1 OSR0  Convertion rate  RMS noise  ENOB^
+ *  X    0    0    0    1        3.52kHz        23uV      17
+ *  X    0    0    1    0        1.76kHz       3.5uV      20
+ *  X    0    0    1    1         880Hz         2uV      21.3
+ *  X    0    1    0    0         440Hz        1.4uV     21.8
+ *  X    0    1    0    1         220Hz         1uV      22.4
+ *  X    0    1    1    0         110Hz        750uV     22.9
+ *  X    0    1    1    1          55Hz        510nV     23.4
+ *  X    1    0    0    0         27.5Hz       375nV      24 
+ *  X    1    0    0    1        13.75Hz       250nV     24.4
+ *  X    1    1    1    1        6.875Hz       200nV     24.6
+ * 
+ * ^ - Effective Number Of Bits
+ */
+
+#define MPC624_SPEED_3_52_kHz   (MPC624_OSR4                                           | MPC624_OSR0)
+#define MPC624_SPEED_1_76_kHz   (MPC624_OSR4                             | MPC624_OSR1              )
+#define MPC624_SPEED_880_Hz     (MPC624_OSR4                             | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_440_Hz     (MPC624_OSR4               | MPC624_OSR2                            )
+#define MPC624_SPEED_220_Hz     (MPC624_OSR4               | MPC624_OSR2               | MPC624_OSR0)
+#define MPC624_SPEED_110_Hz     (MPC624_OSR4               | MPC624_OSR2 | MPC624_OSR1              )
+#define MPC624_SPEED_55_Hz      (MPC624_OSR4               | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_27_5_Hz    (MPC624_OSR4 | MPC624_OSR3                                          )
+#define MPC624_SPEED_13_75_Hz   (MPC624_OSR4 | MPC624_OSR3                             | MPC624_OSR0)
+#define MPC624_SPEED_6_875_Hz   (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+//----------------------------------------------------------------------------
+typedef struct
+{
+  unsigned long int ulConvertionRate;   // set by mpc624_attach() from driver's parameters
+} skel_private;
+
+#define devpriv ((skel_private *)dev->private)
+//----------------------------------------------------------------------------
+static comedi_lrange range_mpc624_bipolar1 =
+{ 
+  1, 
+  {
+//    BIP_RANGE(1.01) // this is correct,
+    // but my MPC-624 actually seems to have a range of 2.02
+    BIP_RANGE(2.02)
+  }
+};
+static comedi_lrange range_mpc624_bipolar10 =
+{ 
+  1,
+  {
+//    BIP_RANGE(10.1) // this is correct,
+    // but my MPC-624 actually seems to have a range of 20.2
+    BIP_RANGE(20.2)
+  }
+};
+//----------------------------------------------------------------------------
+static int mpc624_attach(comedi_device *dev,comedi_devconfig *it);
+static int mpc624_detach(comedi_device *dev);
+//----------------------------------------------------------------------------
+static comedi_driver driver_mpc624 = 
+{
+       driver_name:    "mpc624",
+       module:         THIS_MODULE,
+       attach:         mpc624_attach,
+       detach:         mpc624_detach
+};
+//----------------------------------------------------------------------------
+static int mpc624_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, 
+    lsampl_t *data);
+//----------------------------------------------------------------------------
+static int mpc624_attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+    int iobase;
+
+    iobase = it->options[0];
+       rt_printk("comedi%d: mpc624 [0x%04x, ", dev->minor, iobase);
+    if(request_region(iobase, MPC624_SIZE, "mpc624") == NULL)
+    {
+        rt_printk("I/O port(s) in use\n");
+        return -EIO;
+    }
+
+       dev->iobase = iobase;
+       dev->board_name = "mpc624";
+    
+    // Private structure initialization
+    if(alloc_private(dev, sizeof(skel_private)) < 0)
+       return -ENOMEM;
+
+    switch(it->options[1])
+    {
+      case 0:
+        devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+        rt_printk("3.52 kHz, ");
+        break;
+      case 1:
+        devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
+        rt_printk("1.76 kHz, ");
+        break;
+      case 2:
+        devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
+        rt_printk("880 Hz, ");
+        break;
+      case 3:
+        devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
+        rt_printk("440 Hz, ");
+        break;
+      case 4:
+        devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
+        rt_printk("220 Hz, ");
+        break;
+      case 5:
+        devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
+        rt_printk("110 Hz, ");
+        break;
+      case 6:
+        devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
+        rt_printk("55 Hz, ");
+        break;
+      case 7:
+        devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
+        rt_printk("27.5 Hz, ");
+        break;
+      case 8:
+        devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
+        rt_printk("13.75 Hz, ");
+        break;
+      case 9:
+        devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
+        rt_printk("6.875 Hz, ");
+        break;
+      default:
+        rt_printk("illegal convertion rate setting! Valid numbers are 0..9. Using 9 => 6.875 Hz, ");
+        devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+    }
+
+    // Subdevices structures
+       if(alloc_subdevices(dev, 1) < 0)
+               return -ENOMEM;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE|SDF_DIFF;
+       s->n_chan=8;
+    switch(it->options[1])
+    {
+      default:
+        s->maxdata=0x3FFFFFFF;
+        rt_printk("30 bit, ");
+    }
+
+    switch(it->options[1])
+    {
+      case 0:
+        s->range_table=&range_mpc624_bipolar1;
+        rt_printk("1.01V]: ");
+        break;
+      default:
+        s->range_table=&range_mpc624_bipolar10;
+        rt_printk("10.1V]: ");
+    }
+       s->len_chanlist=1;
+       s->insn_read = mpc624_ai_rinsn;
+
+       rt_printk("attached\n");
+
+       return 1;
+}
+
+static int mpc624_detach(comedi_device *dev)
+{
+       rt_printk("comedi%d: mpc624: remove\n",dev->minor);
+    
+    if(dev->iobase)
+        release_region(dev->iobase, MPC624_SIZE);
+       
+       return 0;
+}
+
+// Timeout 200ms
+#define TIMEOUT 200
+
+static int mpc624_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
+{
+       int n, i;
+    unsigned long int data_in, data_out, j;
+    unsigned char ucPort;
+
+    // WARNING: We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
+       outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
+//    rt_printk("Channel %d: \n", insn->chanspec);
+    if(!insn->n)
+    {
+      rt_printk("MPC624: Warning, no data to aquire\n");
+      return 0;
+    }
+
+       for(n=0; n < insn->n; n++)
+    {
+        // Trigger the convertion
+        outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+        comedi_udelay(1);
+        outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
+        comedi_udelay(1);
+        outb(0, dev->iobase + MPC624_ADC);
+        comedi_udelay(1);
+        
+               // Wait for the convertion to end
+               for(i=0; i<TIMEOUT; i++)
+        {
+            ucPort = inb(dev->iobase + MPC624_ADC);
+                       if(ucPort & MPC624_ADBUSY)
+                comedi_udelay(1000);
+            else
+                break;
+               }
+               if(i == TIMEOUT)
+        {
+                       rt_printk("MPC624: timeout (%dms)\n", TIMEOUT);
+                       data[n] = 0;
+                       return -ETIMEDOUT;
+               }
+
+               // Start reading data
+        data_in = 0;
+        data_out = devpriv->ulConvertionRate;
+        comedi_udelay(1);
+        for(i = 0; i < 32; i++)
+        {
+            // Set the clock low
+            outb(0, dev->iobase + MPC624_ADC);        
+            comedi_udelay(1);
+
+            if(data_out & (1<<31))     // the next bit is a 1
+            {
+                // Set the ADSDI line (send to MPC624)
+                outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
+                comedi_udelay(1);
+                // Set the clock high
+                outb(MPC624_ADSCK | MPC624_ADSDI, dev->iobase + MPC624_ADC);
+            }
+            else                       // the next bit is a 0
+            {
+                // Set the ADSDI line (send to MPC624)
+                outb(0, dev->iobase + MPC624_ADC);
+                comedi_udelay(1);
+                // Set the clock high
+                outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+            }
+            // Read ADSDO on high clock (receive from MPC624)
+            comedi_udelay(1);
+            data_in <<= 1;
+            data_in |= (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
+            comedi_udelay(1);
+
+            data_out <<= 1;
+        }
+        
+        // Received 32-bit long value consist of:
+        //   31: EOC (End Of Transmission) bit - should be 0
+        //   30: DMY (Dummy) bit               - should be 0
+        //   29: SIG (Sign) bit                - 1 if the voltage is positive, 0 if negative
+        //   28: MSB (Most Significant Bit)    - the first bit of convertion result
+        //   ....
+        //   05: LSB (Least Significant Bit)   - the last bit of convertion result
+        //   04: sub-LSB                       - sub-LSBs are basically noise, but when
+        //   03: sub-LSB                         averaged properly, they can increase convertion
+        //   02: sub-LSB                         precision up to 29 bits; they can be discarded 
+        //   01: sub-LSB                         without loss of resolution.
+        //   00: sub-LSB
+
+       if(data_in & MPC624_EOC_BIT)
+               rt_printk("MPC624: EOC bit is set (data_in=%d)!", data_in);
+       if(data_in & MPC624_DMY_BIT)
+               rt_printk("MPC624: DMY bit is set (data_in=%d)!", data_in);
+        if(data_in & MPC624_SGN_BIT)  // check the sign bit
+        { // The voltage is positive
+            data_in &= 0x3FFFFFFF;  // EOC and DMY should be 0, but we will mask them out just to be sure
+            data[n] = data_in;      // comedi operates on unsigned numbers, so we don't clear the SGN bit
+            // SGN bit is still set! It's correct, since we're converting to unsigned.
+        }
+        else
+        { // The voltage is negative
+            // data_in contains a number in 30-bit two's complement code and we must deal with it
+            data_in |= MPC624_SGN_BIT;
+            data_in  = ~data_in;
+            data_in += 1;
+            data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT); 
+                // clear EOC and DMY bits
+            data_in = 0x20000000 - data_in;
+            data[n] = data_in;
+        }
+       }
+
+       // Return the number of samples read/written
+       return n;
+}
+
+COMEDI_INITCLEANUP(driver_mpc624);