📄 icp_multi.c
字号:
/* comedi/drivers/icp_multi.c COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-2002 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: icp_multi.oDescription: Inova ICP_MULTIAuthor: Anne Smorthit <anne.smorthit@sfwte.ch>Devices: [Inova] ICP_MULTI (icp_multi)Status: worksThe driver works for analog input and output and digital input and output.It does not work with interrupts or with the counters. Currently no supportfor DMA.It has 16 single-ended or 8 differential Analogue Input channels with 12-bitresolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Inputranges can be individually programmed for each channel. Voltage or currentmeasurement is selected by jumper.There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V16 x Digital Inputs, 24V8 x Digital Outputs, 24V, 1A4 x 16-bit countersOptions: [0] - PCI bus number - if bus number and slot number are 0, then driver search for first unused card [1] - PCI slot number */#include <linux/comedidev.h>#include <linux/delay.h>#include <linux/pci.h>#include "icp_multi.h"#define DEVICE_ID 0x8000 /* Device ID */#define ICP_MULTI_EXTDEBUG// Hardware types of the cards#define TYPE_ICP_MULTI 0#define IORANGE_ICP_MULTI 32 #define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */#define ICP_MULTI_AI 2 /* R: Analogue input data */#define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */#define ICP_MULTI_AO 6 /* R/W: Analogue output data */#define ICP_MULTI_DI 8 /* R/W: Digital inouts */#define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */#define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */#define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */#define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */#define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */#define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */#define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */#define ICP_MULTI_SIZE 0x20 /* 32 bytes */// Define bits from ADC command/status register#define ADC_ST 0x0001 /* Start ADC */#define ADC_BSY 0x0001 /* ADC busy */#define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */#define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */#define ADC_DI 0x0040 /* Differential input mode 1 = differential */// Define bits from DAC command/status register#define DAC_ST 0x0001 /* Start DAC */#define DAC_BSY 0x0001 /* DAC busy */#define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */#define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */// Define bits from interrupt enable/status registers#define ADC_READY 0x0001 /* A/d conversion ready interrupt */#define DAC_READY 0x0002 /* D/a conversion ready interrupt */#define DOUT_ERROR 0x0004 /* Digital output error interrupt */#define DIN_STATUS 0x0008 /* Digital input status change interrupt */#define CIE0 0x0010 /* Counter 0 overrun interrupt */#define CIE1 0x0020 /* Counter 1 overrun interrupt */#define CIE2 0x0040 /* Counter 2 overrun interrupt */#define CIE3 0x0080 /* Counter 3 overrun interrupt */// Useful definitions#define Status_IRQ 0x00ff // All interrupts// Define analogue rangestatic comedi_lrange range_analog={ 4, { UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5), BIP_RANGE(10) }};static char range_codes_analog[]={0x00, 0x20, 0x10, 0x30};/*============================================================================== Forward declarations==============================================================================*/static int icp_multi_attach(comedi_device *dev, comedi_devconfig *it);static int icp_multi_detach(comedi_device *dev);/*============================================================================== Data & Structure declarations==============================================================================*/static unsigned short pci_list_builded=0; /*=1 list of card is know */typedef struct { char *name; // driver name int device_id; int iorange; // I/O range len char have_irq; // 1=card support IRQ char cardtype; // 0=ICP Multi int n_aichan; // num of A/D chans int n_aichand; // num of A/D chans in diff mode int n_aochan; // num of D/A chans int n_dichan; // num of DI chans int n_dochan; // num of DO chans int n_ctrs; // num of counters int ai_maxdata; // resolution of A/D int ao_maxdata; // resolution of D/A comedi_lrange *rangelist_ai; // rangelist for A/D char *rangecode; // range codes for programming comedi_lrange *rangelist_ao; // rangelist for D/A} boardtype;static boardtype boardtypes[] ={ {"icp_multi", // Driver name DEVICE_ID, // PCI device ID IORANGE_ICP_MULTI, // I/O range length 1, // 1=Card supports interrupts TYPE_ICP_MULTI, // Card type = ICP MULTI 16, // Num of A/D channels 8, // Num of A/D channels in diff mode 4, // Num of D/A channels 16, // Num of digital inputs 8, // Num of digital outputs 4, // Num of counters 0x0fff, // Resolution of A/D 0x0fff, // Resolution of D/A &range_analog, // Rangelist for A/D range_codes_analog, // Range codes for programming &range_analog }, // Rangelist for D/A};#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))static comedi_driver driver_icp_multi={ driver_name: "icp_multi", module: THIS_MODULE, attach: icp_multi_attach, detach: icp_multi_detach, num_names: n_boardtypes, board_name: boardtypes, offset: sizeof(boardtype), };COMEDI_INITCLEANUP(driver_icp_multi);typedef struct{ char valid; // card is usable void *io_addr; // Pointer to mapped io address unsigned long phys_iobase; // Physical io address unsigned int AdcCmdStatus; // ADC Command/Status register unsigned int DacCmdStatus; // DAC Command/Status register unsigned int IntEnable; // Interrupt Enable register unsigned int IntStatus; // Interrupt Status register unsigned int act_chanlist[32]; // list of scaned channel unsigned char act_chanlist_len; // len of scanlist unsigned char act_chanlist_pos; // actual position in MUX list unsigned int *ai_chanlist; // actaul chanlist sampl_t *ai_data; // data buffer sampl_t ao_data[4]; // data output buffer sampl_t di_data; // Digital input data unsigned int do_data; // Remember digital output data} icp_multi_private;#define devpriv ((icp_multi_private *)dev->private)#define this_board ((boardtype *)dev->board_ptr)/* ============================================================================== More forward declarations==============================================================================*/#if 0static int check_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);#endifstatic void setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);static int icp_multi_reset(comedi_device *dev);/* ============================================================================== Functions==============================================================================*//*============================================================================== Name: icp_multi_insn_read_ai Description: This function reads a single analogue input. Parameters: comedi_device *dev Pointer to current device structure comedi_subdevice *s Pointer to current subdevice structure comedi_insn *insn Pointer to current comedi instruction lsampl_t *data Pointer to analogue input data Returns:int Nmuber of instructions executed==============================================================================*/static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data){ int n,timeout;#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");#endif // Disable A/D conversion ready interrupt devpriv->IntEnable &= ~ADC_READY; writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN); // Clear interrupt status devpriv->IntStatus |= ADC_READY; writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT); // Set up appropriate channel, mode and range data, for specified channel setup_channel_list(dev, s, &insn->chanspec, 1);#ifdef ICP_MULTI_EXTDEBUG printk("icp_multi A ST=%4x IO=%x\n",readw(dev->iobase+ICP_MULTI_ADC_CSR), dev->iobase+ICP_MULTI_ADC_CSR);#endif for (n=0; n<insn->n; n++) { // Set start ADC bit devpriv->AdcCmdStatus |= ADC_ST; writew(devpriv->AdcCmdStatus, dev->iobase+ICP_MULTI_ADC_CSR); devpriv->AdcCmdStatus &= ~ADC_ST;#ifdef ICP_MULTI_EXTDEBUG printk("icp multi B n=%d ST=%4x\n",n,readw(dev->iobase+ICP_MULTI_ADC_CSR));#endif comedi_udelay(1);#ifdef ICP_MULTI_EXTDEBUG printk("icp multi C n=%d ST=%4x\n",n,readw(dev->iobase+ICP_MULTI_ADC_CSR));#endif // Wait for conversion to complete, or get fed up waiting timeout=100; while (timeout--) { if (!(readw(dev->iobase+ICP_MULTI_ADC_CSR) & ADC_BSY)) goto conv_finish;#ifdef ICP_MULTI_EXTDEBUG if (!(timeout%10)) printk("icp multi D n=%d tm=%d ST=%4x\n",n,timeout,readw(dev->iobase+ICP_MULTI_ADC_CSR));#endif comedi_udelay(1); } // If we reach here, a timeout has occurred comedi_error(dev,"A/D insn timeout"); // Disable interrupt devpriv->IntEnable &= ~ADC_READY; writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN); // Clear interrupt status devpriv->IntStatus |= ADC_READY; writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT); // Clear data received data[n]=0;#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);#endif return -ETIME;conv_finish: data[n] = (readw(dev->iobase+ICP_MULTI_AI) >> 4 ) & 0x0fff; } // Disable interrupt devpriv->IntEnable &= ~ADC_READY; writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN); // Clear interrupt status devpriv->IntStatus |= ADC_READY; writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);#endif return n;}/*============================================================================== Name: icp_multi_insn_write_ao Description: This function writes a single analogue output. Parameters: comedi_device *dev Pointer to current device structure comedi_subdevice *s Pointer to current subdevice structure comedi_insn *insn Pointer to current comedi instruction lsampl_t *data Pointer to analogue output data Returns:int Nmuber of instructions executed==============================================================================*/static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data){ int n, chan, range, timeout;#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");#endif // Disable D/A conversion ready interrupt devpriv->IntEnable &= ~DAC_READY; writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN); // Clear interrupt status devpriv->IntStatus |= DAC_READY; writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT); // Get channel number and range chan = CR_CHAN(insn->chanspec); range = CR_RANGE(insn->chanspec); // Set up range and channel data // Bit 4 = 1 : Bipolar // Bit 5 = 0 : 5V // Bit 5 = 1 : 10V // Bits 8-9 : Channel number devpriv->DacCmdStatus &= 0xfccf; devpriv->DacCmdStatus |= this_board->rangecode[range]; devpriv->DacCmdStatus |= (chan << 8); writew(devpriv->DacCmdStatus, dev->iobase+ICP_MULTI_DAC_CSR); for (n=0; n<insn->n; n++) { // Wait for analogue output data register to be ready for new data, or get fed up waiting timeout=100; while (timeout--) { if (!(readw(dev->iobase+ICP_MULTI_DAC_CSR) & DAC_BSY)) goto dac_ready;#ifdef ICP_MULTI_EXTDEBUG if (!(timeout%10)) printk("icp multi A n=%d tm=%d ST=%4x\n",n,timeout,readw(dev->iobase+ICP_MULTI_DAC_CSR));#endif comedi_udelay(1); } // If we reach here, a timeout has occurred comedi_error(dev,"D/A insn timeout"); // Disable interrupt devpriv->IntEnable &= ~DAC_READY; writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN); // Clear interrupt status devpriv->IntStatus |= DAC_READY; writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT); // Clear data received devpriv->ao_data[chan]=0;#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);#endif return -ETIME;dac_ready: // Write data to analogue output data register writew(data[n], dev->iobase + ICP_MULTI_AO); // Set DAC_ST bit to write the data to selected channel devpriv->DacCmdStatus |= DAC_ST; writew(devpriv->DacCmdStatus, dev->iobase+ICP_MULTI_DAC_CSR); devpriv->DacCmdStatus &= ~DAC_ST; // Save analogue output data devpriv->ao_data[chan]=data[n]; }#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);#endif return n;}/*============================================================================== Name: icp_multi_insn_read_ao Description: This function reads a single analogue output. Parameters: comedi_device *dev Pointer to current device structure comedi_subdevice *s Pointer to current subdevice structure comedi_insn *insn Pointer to current comedi instruction lsampl_t *data Pointer to analogue output data Returns:int Nmuber of instructions executed==============================================================================*/static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data){ int n,chan; // Get channel number chan = CR_CHAN(insn->chanspec); // Read analogue outputs for (n=0; n<insn->n; n++) data[n]=devpriv->ao_data[chan]; return n;}/*============================================================================== Name: icp_multi_insn_bits_di Description: This function reads the digital inputs. Parameters: comedi_device *dev Pointer to current device structure comedi_subdevice *s Pointer to current subdevice structure comedi_insn *insn Pointer to current comedi instruction lsampl_t *data Pointer to analogue output data Returns:int Nmuber of instructions executed==============================================================================*/static int icp_multi_insn_bits_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ data[1] = readw(dev->iobase + ICP_MULTI_DI); return 2;}/*============================================================================== Name: icp_multi_insn_bits_do Description: This function writes the appropriate digital outputs. Parameters: comedi_device *dev Pointer to current device structure comedi_subdevice *s Pointer to current subdevice structure comedi_insn *insn Pointer to current comedi instruction lsampl_t *data Pointer to analogue output data Returns:int Nmuber of instructions executed==============================================================================*/static int icp_multi_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data){#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");#endif if (data[0]) { s->state &= ~data[0]; s->state |= (data[0] & data[1]); printk("Digital outputs = %4x \n", s->state); writew(s->state, dev->iobase + ICP_MULTI_DO); } data[1] = readw(dev->iobase + ICP_MULTI_DI);#ifdef ICP_MULTI_EXTDEBUG printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");#endif return 2;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -