Driver Development Kit (DDK)

cancel
Showing results for 
Search instead for 
Did you mean: 

What is the required order of operations for the M-series DAQ (PCI-6229)?

I have a PCI-6229 that I've been working on with the newest M-series DDK.  I have things working with the examples.  I compliment the developers on a good product although documentation is currently really lacking for the DDK.  I'm trying to combine an analog input read for multiple channels followed by a analog output write for multiple channels.  I assume that much of the configuration I'm sending to the board is redundant since this is the only program accessing the board and the operations are the same every loop, so I've been cutting out as much as possible but my understanding of the low level operations and their impact on other operations is very limited.  I'm concerned that I may be cutting out necessary parts that may cause glitches sometimes.  Can anyone tell me what functions I need to run every loop and in between the AI read and AO write?  Here is a list of functions I am curious about:

configureTimebase (), pllReset (), analogTriggerReset (), aiReset (), aiPersonalize (), aiClearConfigurationMemory (), aiConfigureChannel (), aiSetFifoRequestMode (), aiEnvironmentalize (), aiHardwareGating (), aiTrigger (), aiSampleStop (), aiNumberOfSamples (), aiSampleStart (),  aiConvert (), etc.

It appears I have to read the EEPROM every set of inputs and outputs to determine the latest scaling factors and such, but I'm wondering if the switching between the AO and AI overwrites the setup of the other (or at least it can't be relied upon).


A second and partially related question is is there any documentation or example code describing how to setup interrupt setup and handling on the M-series devices?

Thanks in advance for everyone's help.
0 Kudos
Message 1 of 11
(10,608 Views)

Hi, I'm new to this DDK prorgamming on m-series cards myself. I've successfully programming the PCI-6254 , a similar card to yours. I've got interrupts, analog reading and counters to work. If I give any bad advise you have been warned!!

 

To do interrupts first you must register your interrupt routine with the operatating system you are using, in my case RTX:

// Attach the interrupt

interrupt_handles[interrupt_count] = RtAttachInterruptVector( NULL, 0, pRoutineIST, this, 127, PCIBus, bus_number, interrupt_level, interrupt_vector);

(note: This is operation system dependant so you will have to specify the OS your using before I can help)

Bear in mind you have to find the IRQ number, bus number and device number before you can register the routine. You get these through scanning the bus when you enumulate all the cards. Email me if you want help with this.

Next you need to enable the appropriate interrupt registers. These determine what causes an interrupt to occur. For interrupts on the end of every scan ->

board->Interrupt_A_Enable.setAI_Error_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_FIFO_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_SC_TC_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_START1_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_START2_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_START_Interrupt_Enable(0);

board->Interrupt_A_Enable.setAI_STOP_Interrupt_Enable(1);

board->Interrupt_A_Enable.flush();

// Next you need to clear any existing interrupts that may have occured

board->Interrupt_A_Ack.setAI_Error_Interrupt_Ack(1);

board->Interrupt_A_Ack.setAI_SC_TC_Error_Confirm(1);

board->Interrupt_A_Ack.setAI_SC_TC_Interrupt_Ack(1);

board->Interrupt_A_Ack.setAI_START1_Interrupt_Ack(1);

board->Interrupt_A_Ack.setAI_START2_Interrupt_Ack(1);

board->Interrupt_A_Ack.setAI_START_Interrupt_Ack(1);

board->Interrupt_A_Ack.setAI_STOP_Interrupt_Ack(1);

board->Interrupt_A_Ack.flush();

Message Edited by studio_uk on 11-29-2005 04:18 AM

Message Edited by studio_uk on 11-29-2005 04:20 AM

Message 2 of 11
(10,588 Views)


// Now you need to enable the appropriate interrupt group

board->Interrupt_Control.setInterrupt_Group_A_Enable(1);

board->Interrupt_Control.setInterrupt_Group_B_Enable(0);

board->Interrupt_Control.flush();

// If you use the FIFO interrupt you need to set what FIFO state will cause an interrupt

board->AI_Mode_3.setAI_FIFO_Mode(tMSeries::tAI_Mode_3::kAI_FIFO_ModeNot_Empty);

board->AI_Mode_3.flush();

Then you arm the card and interrupts will start being created.

Bear in mind, some interrupts require you to call one of the "board->Interrupt_A_Ack.writeAI_XXX_Interrupt_Ack(1);" or only one interrupt will be triggered. I beleive FIFO interrupts are the only ones where you don't need to do this.

 

// As for these functions

configureTimebase (), pllReset (), analogTriggerReset (), aiReset (), aiPersonalize (), aiClearConfigurationMemory (), aiConfigureChannel (), aiSetFifoRequestMode (), aiEnvironmentalize (), aiHardwareGating (), aiTrigger (), aiSampleStop (), aiNumberOfSamples (), aiSampleStart (),  aiConvert (),

These all setup the card for the specific task you want to perform with the card. The first thing to do is to reset the card. Bear in mind from my experience you have to reset "everything". That includes timers and analog out. If you don't it doesnt seem to work properly, I wouldnt cut any of this code out of your program. As for configuring the card, the order of running each of the functions you've listed is very important. Follow the examples, if you get the order wrong or miss a function call it won't work. The E-Series DDK manual is very well documented. The M and E-Series cards are very similar in operation however the registers are very different. So reading that should shed some light on why all these functions are required.

aiClearConfiguationMemory() and aiConfigureChannel: These functions setup the scanning order. I.e. The card will process every channel you add via "aiConfigureChannel" until they have all been processed, then start again (on the M-Series card you have a maximum of 4096 entries). The results of the scans will be stored in the AI FIFO. Which you can read either via "board->AI_FIFO_Data.readRegister ()" or via a DMA transfer. Note, you do "not" need to reread the EPROM every set of inputs / outputs. The values will not change. Also note the scaling constants are the same for every channel because the card only has one DAC.

One final suggestion is don't remove any functions to save code size/comprexity. They are all needed.

You can contact me on john@itsolutions-uk.net if you have any more questions as this board doesnt get looked at very often.

 

Regards

 

John

Message 3 of 11
(10,590 Views)
Hi,

Just wanted to add a quick note about the eeprom read:

Ideally you should read the eeprom once during device initialization and cache it's contents.  To read the eeprom, bar1 is remapped making all the STC register unavailable.  Strange things can happen if there's another tasks accessing the device when the remapping occurs.  However, you won't corrupt the eeprom since it's write protected.

Note that aiGetScalingCoefficients only parses the raw eeprom data passed as an argument, it does not read the eeprom.

Also, I think aiPersonalize is not affected by AI_Reset.

I'm curious about what John said of resetting AI affecting other operations (AO and CTRs).  I don't think this has to be the case, but at this point I don't have enough information to be sure.  I'll do some more testing and let you know.

Diego.
Message 4 of 11
(10,573 Views)
This is exactly the information I was looking for.  It'll be a few days before I get all of this sorted out and debugged, but have couple questions of clarification now.

Diego, you said I would just cache the results from the EEPROM, but the scaling coefficients can potentially change as the board warms up etc. so shouldn't the EEPROM be read pretty frequently to avoid losing calibration?

Is there any advantage to trying to implement DMA transfers if the board is only sending a single-point acquisition?  Ideally, I would like to setup hardware timed acquisition around 1kHz and then the program read the latest AI readings from a FIFO and write the next AOs to a FIFO, but my understanding is that the card basically has to be reconfigured between AI, AO, and DIO events.  Is that correct?

Thanks for everyone's input so far.  It has been extremely helpful.

Aaron
0 Kudos
Message 5 of 11
(10,571 Views)
Hi Aaron,

The calibration constants won't change.  These are store when the board is externally calibrated at the factory.  Any temperature correction is done by the driver.  In DAQmx this is what's called self-calibration.  At the moment, MHDDK doesn't have any examples on self-calibration.

Regarding your application, it seems that you are trying to do some sort of control loop where you get 1 point from each channel, process (pid or something) and output the response.  If that's the case, you don't need to reconfigure the hardware for each point.  It's all one big task!.  Here's an outline of what you would do for AI and AO:

- Put the device in a known state: reset AI, AO
- Configure AO for on-demand output.  In this mode when you write a value to the DACs it goes out inmediately.
- Configure AI for continuous acquisition.  Set up a interrupt source to determine when there's data avaliable.  You can use the FIFO condition interrupt or the scan clock interrupt.
- How you handle the interrupt and where you proccess the data depends on you system.  Assuming you process everything in the ISR:
    - Get the latest sample(s) from the FIFO by emptying it
    - Process the data point
    - Write the response to the DAC(s).  Output is generated inmediately
- Goes on forever...

In DAQmx terminology you would be doing a Hardware-Timed Single Point acquisition.

If you are using static DIO, just configure the port and use it.  It's pretty much independent of AI and AO.

For a 1kHz loop is not worth setting up DMA.  Most systems can handle that rate easily.  You'll probably get less performance trying to determine where the DMA is at.

Hope it helps!,
Diego.

Message 6 of 11
(10,565 Views)
I have finally resolved most issues, advanced my understanding greatly, and almost completed the interrupt-based program.  I appear to have one major outstanding issue that someone can hopefullly help me with.  When I enable the ISR,  the program basically goes into an infinite loop trying to service the interrupt but since the ISR doesn't appear to properly handle the interrupt, it gets called immediately again.
 
I've written a small test program that looks at the interrupt status bits after doing various things.  InterruptStatus prints the results of AI_Status_1.readRegister(), AO_Status_1.readRegister(), AO_Status_2.readRegister(), and AI_Status_1.readAI_FIFO_Empty_St() in binary.
 
InterruptStatus();
// responds "AI Status 1 = 110010, AO Status 1 = 1000000000010, AO Status 2 = 0, readAI_FIFO_Empty_St = 0"

board->Interrupt_A_Ack.setAI_Error_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_SC_TC_Error_Confirm(1);
board->Interrupt_A_Ack.setAI_SC_TC_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START1_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START2_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_STOP_Interrupt_Ack(1);
board->Interrupt_A_Ack.flush();


board->Interrupt_B_Ack.setAO_Error_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_STOP_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_START_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_UPDATE_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_START1_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_BC_TC_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_UC_TC_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_STOP_Interrupt_Ack(1);
board->Interrupt_B_Ack.flush();

InterruptStatus();

// responds "AI Status 1 = 10, AO Status 1 = 1000000000010, AO Status 2 = 0, readAI_FIFO_Empty_St = 0"

board->Interrupt_A_Enable.setAI_Error_Interrupt_Enable(0);
board->Interrupt_A_Enable.setAI_FIFO_Interrupt_Enable(0); //was 0
board->Interrupt_A_Enable.setAI_SC_TC_Interrupt_Enable(0);
board->Interrupt_A_Enable.setAI_START1_Interrupt_Enable(0);
board->Interrupt_A_Enable.setAI_START2_Interrupt_Enable(0);
board->Interrupt_A_Enable.setAI_START_Interrupt_Enable(0);
board->Interrupt_A_Enable.setAI_STOP_Interrupt_Enable(0);
board->Interrupt_A_Enable.flush();

board->Interrupt_B_Enable.setAO_BC_TC_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_START1_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_UPDATE_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_START_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_STOP_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_Error_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_UC_TC_Interrupt_Enable(0);
board->Interrupt_B_Enable.setAO_FIFO_Interrupt_Enable(0);
board->Interrupt_B_Enable.setG1_TC_Interrupt_Enable(0);
board->Interrupt_B_Enable.setG1_Gate_Interrupt_Enable(0);
board->Interrupt_B_Enable.setPass_Thru_1_Interrupt_Enable(0);
board->Interrupt_B_Enable.flush();

InterruptStatus();

// responds "AI Status 1 = 10, AO Status 1 = 1000000000010, AO Status 2 = 0, readAI_FIFO_Empty_St = 0"

aiClearFifo(board);

board->Interrupt_Control.setInterrupt_Group_A_Enable(0);
board->Interrupt_Control.setInterrupt_Group_B_Enable(0);
board->Interrupt_Control.flush();

InterruptStatus();

// responds "AI Status 1 = 1000000000000, AO Status 1 = 1000000000010, AO Status 2 = 0, readAI_FIFO_Empty_St = 1"

board->Interrupt_A_Ack.setAI_Error_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_SC_TC_Error_Confirm(1);
board->Interrupt_A_Ack.setAI_SC_TC_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START1_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START2_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_START_Interrupt_Ack(1);
board->Interrupt_A_Ack.setAI_STOP_Interrupt_Ack(1);
board->Interrupt_A_Ack.flush();

board->Interrupt_B_Ack.setAO_Error_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_STOP_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_START_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_UPDATE_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_START1_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_BC_TC_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_UC_TC_Interrupt_Ack(1);
board->Interrupt_B_Ack.setAO_STOP_Interrupt_Ack(1);
board->Interrupt_B_Ack.flush();

InterruptStatus();

0 Kudos
Message 7 of 11
(10,242 Views)

// responds "AI Status 1 = 1000000000000, AO Status 1 = 1000000000010, AO Status 2 = 0x0, readAI_FIFO_Empty_St = 1"

 
My ISR has the Group B interrupt disabled, Group A interrupt enabled and FIFO_Mode set to AI_FIFO_ModeNot_Empty.  My program has neither AI and AO tasks running currently, but the ISR is in a race condition.  All of the code above has no tasks running and comes right after a board->reset().
 
Hopefully none of the AO_Status bits are causing my interrupts.  The 13th bit of AI_Status appears to be kAI_FIFO_Empty_StId, which I assume means the FIFO is empty if set.  Is this possibly causing the interrupt.  I only want to assert an interrupt if the FIFO is NOT empty.
 
I'm testing this out using Tenasys' InTime real time OS.  If anybody sees what I'm missing, I would greatly appreciate a pointer.  Thanks in advance.
 
Aaron
0 Kudos
Message 8 of 11
(10,241 Views)
What are your pooling frequency?
 
I've had a similar problem of race conditions in my interrupt which was caused because I was generating data too quickly. As soon as the ISR left more input was waiting so would be triggered again. The way I fixed this was to reduce my fixed pooling frequency , later I decided to move to dynamic data aquisition where the convertion rate varied depending on the current situation. This maintained response but allows maximum convertion rate.
 
Post your convertion register settings and i'll take a look.
0 Kudos
Message 9 of 11
(10,239 Views)

My polling frequency was 0.1 Hz.  I believe I found my problem, which I probably should have picked up sooner because of the code you supplied earlier.  I set:

board->Interrupt_A_Enable.setAI_FIFO_Interrupt_Enable(1);

and

board->Interrupt_A_Enable.setAI_STOP_Interrupt_Enable(0);

I thought the FIFO would interrupt whenever the FIFO was not empty, but it appears to be the opposite.  When I instead enable the STOP interrupt, everything works normally.  Is this true or do you think there is some subtle mistake somewhere else?  Thanks.

0 Kudos
Message 10 of 11
(10,215 Views)