📄 amplc_pci230.c
字号:
if(tmp!=cmd->convert_arg)err++; } if(err)return 4; return 0;}static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s){ int i, chan, range, diff; unsigned int adccon, adcen, adcg; /* Get the command. */ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; /* Calculate number of conversions required. */ if(cmd->stop_src == TRIG_COUNT) { devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len; devpriv->ai_stop = 0; } else { /* TRIG_NONE, user calls cancel. */ devpriv->ai_count = 0; devpriv->ai_stop = 1; } /* Steps; * - Disable ADC interrupts. * - Enable and reset FIFO, specify uni/bip, se/diff, and start conversion source to none. * - Set channel scan list. * - Set channel gains. * - Enable conversion complete interrupt. * - Set the counter timers to the specified sampling frequency. * - Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 0. */ /* Disable ADC interrupt. */ devpriv->ier &= ~PCI230_INT_ADC; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); if (CR_AREF(cmd->chanlist[0])==AREF_DIFF) { /* Differential - all channels must be differential. */ diff = 1; adccon = PCI230_ADC_IM_DIF; } else { /* Single ended - all channels must be single-ended. */ diff = 0; adccon = PCI230_ADC_IM_SE; } adccon |= PCI230_ADC_FIFO_RESET | PCI230_ADC_FIFO_EN; adcg = 0; adcen = 0; /* If bit 2 of range unset, range is referring to bipolar element in range table */ range = CR_RANGE(cmd->chanlist[0]); devpriv->ai_bipolar = !PCI230_TEST_BIT(range, 2); if (devpriv->ai_bipolar) { adccon |= PCI230_ADC_IR_BIP; for (i = 0; i < cmd->chanlist_len; i++) { chan = CR_CHAN(cmd->chanlist[i]); range = CR_RANGE(cmd->chanlist[i]); if (diff) { adcg |= range<<(2*chan-2*chan%2); adcen |= 3<<2*chan; } else { adcg |= range<<(chan-chan%2); adcen |= 1<<chan; } } } else { adccon |= PCI230_ADC_IR_UNI; for (i = 0; i < cmd->chanlist_len; i++) { chan = CR_CHAN(cmd->chanlist[i]); range = CR_RANGE(cmd->chanlist[i]); if (diff) { adcg |= ((range&(~4))+1)<<(2*chan-2*chan%2); adcen |= 3<<2*chan; } else { adcg |= ((range&(~4))+1)<<(chan-chan%2); adcen |= 1<<chan; } } } /* Enable and reset FIFO, specify FIFO trigger level full, specify uni/bip, se/diff, and start conversion source to none. */ outw(adccon | PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON); /* Set channel scan list. */ outw(adcen, dev->iobase + PCI230_ADCEN); /* Set channel gains. */ outw(adcg, dev->iobase + PCI230_ADCG); /* Enable ADC (conversion complete) interrupt. */ devpriv->ier |= PCI230_INT_ADC; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); /* Set start conversion source. */ if(cmd->convert_src == TRIG_TIMER) { /* Onboard counter/timer 0. */ adccon = adccon | PCI230_ADC_TRIG_Z2CT0; /* Set the counter timers to the specified sampling frequency. */ pci230_z2_ct0(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); /* cmd->convert_arg is sampling period in ns */ } else { /* TRIG_EXT - external trigger. */ if (cmd->convert_arg) { /* Trigger on +ve edge. */ adccon = adccon | PCI230_ADC_TRIG_EXTP; } else { /* Trigger on -ve edge. */ adccon = adccon | PCI230_ADC_TRIG_EXTN; } } /* Set FIFO interrupt trigger level. */ if(cmd->stop_src == TRIG_COUNT) { if (devpriv->ai_count < 2048) { adccon = adccon | PCI230_ADC_INT_FIFO_NEMPTY; } else { adccon = adccon | PCI230_ADC_INT_FIFO_HALF; } } else { /* TRIG_NONE - trigger on half-full FIFO. */ adccon = adccon | PCI230_ADC_INT_FIFO_HALF; } outw(adccon, dev->iobase + PCI230_ADCCON); return 0;}/* This function doesn't require a particular form, this is just * what happens to be used in some of the drivers. It should * convert ns nanoseconds to a counter value suitable for programming * the device. Also, it should adjust ns so that it cooresponds to * the actual time that the device will use. */static void pci230_ns_to_timer(unsigned int *ns,int round){ unsigned int divisor0, divisor1; i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &divisor0, &divisor1, ns, TRIG_ROUND_MASK); return;}/* * Set ZCLK_CT0 to square wave mode with period of ns. * Default clk source for ADC. */static void pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round){ /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor2, &devpriv->divisor0, ns, TRIG_ROUND_MASK); /* Generic i8254_load calls; program counters' divide ratios. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3); /* Counter 2, divisor2, square wave (8254 mode 3). */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3); /* Counter 0, divisor0, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 2's input clock source. */ outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 0's input clock source. */ return;}static void pci230_cancel_ct0(comedi_device *dev){ devpriv->divisor2 = 0; devpriv->divisor0 = 0; i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0); /* Counter 2, divisor2, 8254 mode 0. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0); /* Counter 0, divisor0, 8254 mode 0. */}/* * Set ZCLK_CT1 to square wave mode with period of ns. * Default clk source for DAC. */static void pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round){ /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor0, &devpriv->divisor1, ns, TRIG_ROUND_MASK); /* Generic i8254_load calls; program counters' divide ratios. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3); /* Counter 0, divisor0, square wave (8254 mode 3). */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3); /* Counter 1, divisor1, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 0's input clock source. */ outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 1's input clock source. */ return;}static void pci230_cancel_ct1(comedi_device *dev){ devpriv->divisor0 = 0; devpriv->divisor1 = 0; i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0); /* Counter 0, divisor0, 8254 mode 0. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0); /* Counter 1, divisor1, 8254 mode 0. */}/* * Set ZCLK_CT2 to square wave mode with period of ns. * Default clk source for external freq. generator (COMEDI_SUBD_TIMER). */static void pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round){ /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor1, &devpriv->divisor2, ns, TRIG_ROUND_MASK); /* Generic i8254_load calls; program counters' divide ratios. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3); /* Counter 1, divisor1, square wave (8254 mode 3). */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3); /* Counter 2, divisor2, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 1's input clock source. */ outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 2's input clock source. */ return;}static void pci230_cancel_ct2(comedi_device *dev){ devpriv->divisor1 = 0; devpriv->divisor2 = 0; i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0); /* Counter 1, divisor1, 8254 mode 0. */ i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0); /* Counter 2, divisor2, 8254 mode 0. */}/* Interrupt handler */static void pci230_interrupt(int irq, void *d, struct pt_regs *regs){ int status_int; comedi_device *dev = (comedi_device*) d; comedi_subdevice *s; /* Read interrupt status/enable register. */ status_int = inb(devpriv->pci_iobase + PCI230_INT_SCE); /* Disable all of board's interrupts. * (Only those interrrupts that need re-enabling, are, later in the handler). */ devpriv->ier = PCI230_INT_DISABLE; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); /* * Check the source of interrupt and handle it. * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3 interrupts. * However, at present (Comedi-0.7.60) does not allow concurrent * execution of commands, instructions or a mixture of the two. */ if (status_int == PCI230_INT_DISABLE) { printk("comedi%d: amplc_pci230::pci230_interrupt spurious interrupt",dev->minor); } if (status_int & PCI230_INT_ZCLK_CT1) { s = dev->write_subdev; s->async->events = 0; pci230_handle_ao(dev, s); comedi_event(dev, s, s->async->events); s->async->events = 0; } if (status_int & PCI230_INT_ADC) { s = dev->read_subdev; s->async->events = 0; pci230_handle_ai(dev, s); comedi_event(dev, s, s->async->events); s->async->events = 0; } return;}static void pci230_handle_ao(comedi_device *dev, comedi_subdevice *s) { sampl_t data; int i, ret; comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; for (i = 0; i < cmd->chanlist_len; i++) { /* Read sample from Comedi's circular buffer. */ ret = comedi_buf_get(s->async, &data); if(ret < 0) { comedi_error(dev, "buffer underrun"); return; // XXX does comedi_buf_get set s->async->events with appropriate flags in this instance? } /* Write value to DAC. */ pci230_ao_write(dev, data, cmd->chanlist[i]); if(async->cmd.stop_src == TRIG_COUNT) { if(devpriv->ao_count > 0) devpriv->ao_count--; if(devpriv->ao_count == 0) break; } } if(devpriv->ao_count == 0 && devpriv->ao_stop == 0) { /* End of DAC. */ async->events |= COMEDI_CB_EOA; pci230_ao_cancel(dev, s); } else { /* More samples required, tell Comedi to block. */ async->events |= COMEDI_CB_BLOCK; /* Enable DAC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */ devpriv->ier |= PCI230_INT_ZCLK_CT1; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); } return;}static void pci230_handle_ai(comedi_device *dev, comedi_subdevice *s) { int error = 0; int status_fifo; /* Read FIFO state. */ status_fifo = inw(dev->iobase + PCI230_ADCCON); if (status_fifo & PCI230_ADC_FIFO_FULL) { /* Report error otherwise FIFO overruns will go unnoticed by the caller. */ comedi_error(dev, "FIFO overrun"); error++; } else if (status_fifo & PCI230_ADC_FIFO_HALF) { /* FIFO is at least half full. */ pci230_handle_fifo_half_full(dev, s); } else if (status_fifo & PCI230_ADC_FIFO_EMPTY) { /* FIFO empty but we got an interrupt. */ printk("comedi%d: amplc_pci230::pci230_handle_ai FIFO empty - spurious interrupt\n",dev->minor); } else { /* FIFO is less than half full, but not empty. */ pci230_handle_fifo_not_empty(dev, s); } if (error) { /* Cancel sampled conversion. */ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; pci230_ai_cancel(dev, s); } if(devpriv->ai_count == 0 && devpriv->ai_stop == 0) { /* Acquisition complete. */ s->async->events |= COMEDI_CB_EOA; pci230_ai_cancel(dev, s); /* disable hardware conversions */ } else { /* More samples required, tell Comedi to block. */ s->async->events |= COMEDI_CB_BLOCK; /* Enable ADC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */ devpriv->ier |= PCI230_INT_ADC; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); } return;}static void pci230_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) { int i; for (i = 0; i < 2048; i++) { /* Read sample and store in Comedi's circular buffer. */ comedi_buf_put(s->async, pci230_ai_read(dev)); if(s->async->cmd.stop_src == TRIG_COUNT) { if(--devpriv->ai_count == 0) { /* Acquisition complete. */ return; } } } /* More samples required. */ return;}static void pci230_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) { while (devpriv->ai_count != 0) { if (inw(dev->iobase + PCI230_ADCCON) & PCI230_ADC_FIFO_EMPTY) { /* The FIFO is empty, block. */ return; } /* There are sample(s) to read from FIFO, read one and store in Comedi's circular buffer. */ comedi_buf_put(s->async, pci230_ai_read(dev)); if(devpriv->ai_count > 0) devpriv->ai_count--; } /* Acquisition complete. */ return;}static int pci230_ao_cancel(comedi_device *dev, comedi_subdevice *s) { devpriv->ao_count = 0; devpriv->ao_stop = 0; /* Stop counter/timers. */ pci230_cancel_ct1(dev); /* Disable DAC interrupt. */ devpriv->ier &= ~PCI230_INT_ZCLK_CT1; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); return 0;}static int pci230_ai_cancel(comedi_device *dev, comedi_subdevice *s) { devpriv->ai_count = 0; devpriv->ai_stop = 0; /* Stop counter/timers. */ pci230_cancel_ct0(dev); /* Disable ADC interrupt. */ devpriv->ier &= ~PCI230_INT_ADC; outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); /* Reset FIFO and set start conversion source to none. */ outw(PCI230_ADC_FIFO_RESET | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON); /* Clear channel scan list. */ outw(0x0000, dev->iobase + PCI230_ADCEN); /* Clear channel gains. */ outw(0x0000, dev->iobase + PCI230_ADCG); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -