📄 das1800.c
字号:
case TRIG_EXT: control_a |= TGEN | CGSL; break; case TRIG_NOW: control_a |= CGEN; break; default: break; } return control_a;}// returns appropriate bits for control register c, depending on commandstatic int control_c_bits(comedi_cmd cmd){ int control_c; int aref; /* set clock source to internal or external, select analog reference, * select unipolar / bipolar */ aref = CR_AREF(cmd.chanlist[0]); control_c = UQEN; //enable upper qram addresses if(aref != AREF_DIFF) control_c |= SD; if(aref == AREF_COMMON) control_c |= CMEN; /* if a unipolar range was selected */ if(CR_RANGE(cmd.chanlist[0]) & UNIPOLAR) control_c |= UB; switch(cmd.scan_begin_src) { case TRIG_FOLLOW: // not in burst mode switch(cmd.convert_src) { case TRIG_TIMER: /* trig on cascaded counters */ control_c |= IPCLK; break; case TRIG_EXT: /* trig on falling edge of external trigger */ control_c |= XPCLK; break; default: break; } break; case TRIG_TIMER: // burst mode with internal pacer clock control_c |= BMDE | IPCLK; break; case TRIG_EXT: // burst mode with external trigger control_c |= BMDE | XPCLK; break; default: break; } return control_c;}// sets up countersstatic int setup_counters(comedi_device *dev, comedi_cmd cmd){ // setup cascaded counters for conversion/scan frequency switch(cmd.scan_begin_src) { case TRIG_FOLLOW: // not in burst mode if(cmd.convert_src == TRIG_TIMER) { /* set conversion frequency */ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd.convert_arg), cmd.flags & TRIG_ROUND_MASK); if(das1800_set_frequency(dev) < 0) { return -1; } } break; case TRIG_TIMER: // in burst mode /* set scan frequency */ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd.scan_begin_arg), cmd.flags & TRIG_ROUND_MASK); if(das1800_set_frequency(dev) < 0) { return -1; } break; default: break; } // setup counter 0 for 'about triggering' if(cmd.stop_src == TRIG_EXT) { // load counter 0 in mode 0 i8254_load(dev->iobase + DAS1800_COUNTER, 0, 1, 0); } return 0;}// sets up dmastatic void setup_dma(comedi_device *dev, comedi_cmd cmd){ unsigned long lock_flags; const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL; if((devpriv->irq_dma_bits & DMA_ENABLED) == 0) return; /* determine a reasonable dma transfer size */ devpriv->dma_transfer_size = suggest_transfer_size(&cmd); lock_flags = claim_dma_lock(); disable_dma(devpriv->dma0); /* clear flip-flop to make sure 2-byte registers for * count and address get set correctly */ clear_dma_ff(devpriv->dma0); set_dma_addr(devpriv->dma0, virt_to_bus(devpriv->ai_buf0)); // set appropriate size of transfer set_dma_count(devpriv->dma0, devpriv->dma_transfer_size); devpriv->dma_current = devpriv->dma0; devpriv->dma_current_buf = devpriv->ai_buf0; enable_dma(devpriv->dma0); // set up dual dma if appropriate if(dual_dma) { disable_dma(devpriv->dma1); /* clear flip-flop to make sure 2-byte registers for * count and address get set correctly */ clear_dma_ff(devpriv->dma1); set_dma_addr(devpriv->dma1, virt_to_bus(devpriv->ai_buf1)); // set appropriate size of transfer set_dma_count(devpriv->dma1, devpriv->dma_transfer_size); enable_dma(devpriv->dma1); } release_dma_lock(lock_flags); return;}// programs channel/gain list into cardstatic void program_chanlist(comedi_device *dev, comedi_cmd cmd){ int i, n, chan_range; unsigned long irq_flags; const int range_mask = 0x3; //masks unipolar/bipolar bit off range const int range_bitshift = 8; n = cmd.chanlist_len; // spinlock protects indirect addressing comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */ outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*set QRAM address start */ /* make channel / gain list */ for(i = 0; i < n; i++) { chan_range = CR_CHAN(cmd.chanlist[i]) | ((CR_RANGE(cmd.chanlist[i]) & range_mask) << range_bitshift); outw(chan_range, dev->iobase + DAS1800_QRAM); } outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */ comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); return;}// analog input do_cmdstatic int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s){ int ret; int control_a, control_c; comedi_async *async = s->async; comedi_cmd cmd = async->cmd; if(!dev->irq) { comedi_error(dev, "no irq assigned for das-1800, cannot do hardware conversions"); return -1; } /* disable dma on TRIG_WAKE_EOS, or TRIG_RT * (because dma in handler is unsafe at hard real-time priority) */ if(cmd.flags & (TRIG_WAKE_EOS | TRIG_RT)) { devpriv->irq_dma_bits &= ~DMA_ENABLED; }else { devpriv->irq_dma_bits |= devpriv->dma_bits; } // interrupt on end of conversion for TRIG_WAKE_EOS if(cmd.flags & TRIG_WAKE_EOS) { // interrupt fifo not empty devpriv->irq_dma_bits &= ~FIMD; }else { // interrupt fifo half full devpriv->irq_dma_bits |= FIMD; } // determine how many conversions we need if(cmd.stop_src == TRIG_COUNT) { devpriv->count = cmd.stop_arg * cmd.chanlist_len; } das1800_cancel(dev, s); // determine proper bits for control registers control_a = control_a_bits(cmd); control_c = control_c_bits(cmd); /* setup card and start */ program_chanlist(dev, cmd); ret = setup_counters(dev, cmd); if(ret < 0) { comedi_error(dev, "Error setting up counters"); return ret; } setup_dma(dev, cmd); outb(control_c, dev->iobase + DAS1800_CONTROL_C); // set conversion rate and length for burst mode if(control_c & BMDE) { // program conversion period with number of microseconds minus 1 outb(cmd.convert_arg / 1000 - 1, dev->iobase + DAS1800_BURST_RATE); outb(cmd.chanlist_len - 1, dev->iobase + DAS1800_BURST_LENGTH); } outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); // enable irq/dma outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ return 0;}/* read analog input */static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ int i, n; int chan, range, aref, chan_range; int timeout = 1000; short dpnt; int conv_flags = 0; unsigned long irq_flags; /* set up analog reference and unipolar / bipolar mode */ aref = CR_AREF(insn->chanspec); conv_flags |= UQEN; if(aref != AREF_DIFF) conv_flags |= SD; if(aref == AREF_COMMON) conv_flags |= CMEN; /* if a unipolar range was selected */ if(CR_RANGE(insn->chanspec) & UNIPOLAR) conv_flags |= UB; outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */ outb(FFEN, dev->iobase + DAS1800_CONTROL_A); chan = CR_CHAN(insn->chanspec); /* mask of unipolar/bipolar bit from range */ range = CR_RANGE(insn->chanspec) & 0x3; chan_range = chan | (range << 8); comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /* set QRAM address start */ outw(chan_range, dev->iobase + DAS1800_QRAM); outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */ outb(ADC, dev->iobase + DAS1800_SELECT); /* select ADC for baseAddress + 0x0 */ for(n = 0; n < insn->n; n++) { /* trigger conversion */ outb(0, dev->iobase + DAS1800_FIFO); for(i = 0; i < timeout; i++) { if(inb(dev->iobase + DAS1800_STATUS) & FNE) break; } if(i == timeout) { comedi_error(dev, "timeout"); return -ETIME; } dpnt = inw(dev->iobase + DAS1800_FIFO); /* shift data to offset binary for bipolar ranges */ if((conv_flags & UB) == 0) dpnt += 1 << (thisboard->resolution - 1); data[n] = dpnt; } comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); return n;}/* writes to an analog output channel */static int das1800_ao_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ int chan = CR_CHAN(insn->chanspec);// int range = CR_RANGE(insn->chanspec); int update_chan = thisboard->ao_n_chan - 1; short output; unsigned long irq_flags; // card expects two's complement data output = data[0] - (1 << (thisboard->resolution - 1)); // if the write is to the 'update' channel, we need to remember its value if(chan == update_chan) devpriv->ao_update_bits = output; // write to channel comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); outb(DAC(chan), dev->iobase + DAS1800_SELECT); /* select dac channel for baseAddress + 0x0 */ outw(output, dev->iobase + DAS1800_DAC); // now we need to write to 'update' channel to update all dac channels if(chan != update_chan) { outb(DAC(update_chan), dev->iobase + DAS1800_SELECT); /* select 'update' channel for baseAddress + 0x0 */ outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC); } comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); return 1;}/* reads from digital input channels */static int das1800_di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ data[1] = inb(dev->iobase + DAS1800_DIGITAL) & 0xf; data[0] = 0; return 2;}/* writes to digital output channels */static int das1800_do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ lsampl_t wbits; // only set bits that have been masked data[0] &= (1 << s->n_chan) - 1; wbits = devpriv->do_bits; wbits &= ~data[0]; wbits |= data[0] & data[1]; devpriv->do_bits = wbits; outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL); data[1] = devpriv->do_bits; return 2;}/* loads counters with divisor1, divisor2 from private structure */static int das1800_set_frequency(comedi_device *dev){ int err = 0; // counter 1, mode 2 if(i8254_load(dev->iobase + DAS1800_COUNTER, 1, devpriv->divisor1, 2)) err++; // counter 2, mode 2 if(i8254_load(dev->iobase + DAS1800_COUNTER, 2, devpriv->divisor2, 2)) err++; if(err) return -1; return 0;}/* converts requested conversion timing to timing compatible with * hardware, used only when card is in 'burst mode' */static unsigned int burst_convert_arg(unsigned int convert_arg, int round_mode){ unsigned int micro_sec; // in burst mode, the maximum conversion time is 64 microseconds if(convert_arg > 64000) convert_arg = 64000; // the conversion time must be an integral number of microseconds switch(round_mode) { case TRIG_ROUND_NEAREST: default: micro_sec = (convert_arg + 500) / 1000; break; case TRIG_ROUND_DOWN: micro_sec = convert_arg / 1000; break; case TRIG_ROUND_UP: micro_sec = (convert_arg - 1) / 1000 + 1; break; } // return number of nanoseconds return micro_sec * 1000;}// utility function that suggests a dma transfer size based on the conversion period 'ns'static unsigned int suggest_transfer_size(comedi_cmd *cmd){ unsigned int size = DMA_BUF_SIZE; static const int sample_size = 2; // size in bytes of one sample from board unsigned int fill_time = 300000000; // target time in nanoseconds for filling dma buffer unsigned int max_size; // maximum size we will allow for a transfer // make dma buffer fill in 0.3 seconds for timed modes switch(cmd->scan_begin_src) { case TRIG_FOLLOW: // not in burst mode if(cmd->convert_src == TRIG_TIMER) size = (fill_time / cmd->convert_arg) * sample_size; break; case TRIG_TIMER: size = (fill_time / (cmd->scan_begin_arg * cmd->chanlist_len)) * sample_size; break; default: size = DMA_BUF_SIZE; break; } // set a minimum and maximum size allowed max_size = DMA_BUF_SIZE; // if we are taking limited number of conversions, limit transfer size to that if(cmd->stop_src == TRIG_COUNT && cmd->stop_arg * cmd->chanlist_len * sample_size < max_size) max_size = cmd->stop_arg * cmd->chanlist_len * sample_size; if(size > max_size) size = max_size; if(size < sample_size) size = sample_size; return size;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -