Driver Development Kit (DDK)

cancel
Showing results for 
Search instead for 
Did you mean: 

How to develop a driver for NI 6601 under RTX

Hi everyone,

 

I am writing a driver for NI 6601 under RTX using vs2005. There is an example in NI's web. And I also read Register Level Programming Manual carefully.  My encoder is Reinashaw. Using counter is counter0. I have been connecting my encoder signals to 6601 device as belows:

enconder signal A  connected to PFI 39

enconder signal B  connected to PFI 37

enconder signal Z  connected to PFI 38

And NI's examlpe as below:

 

/*
*  gpct_ex7.cpp
*
*  Quadrature encoding
*
*  Read a quadrature encoder using counter 0.  The counter is configured for X4 mode
*  The initial value is loaded into the counter from the Load A register.
*
*  Attach the quadrature encoder's A signal to pin 2 (PFI 39) and the B signal to
*  pin 40 (PFI 37).
*
*  $DateTime: 2006/07/27 23:51:45 $
*
*/

#include "stdio.h"
#include "osiBus.h"
#include "tTIO.h"

void test(iBus *bus);
void initMite(iBus *bus);

int main()
{
 iBus* bus;
 
 //Each PCI card has an identifying 32bit PCI ID number.
 //0x10930000 is the manufacturer ID number for all National Instruments cards
 //0x00001310 is the device ID number for the PCI-6602
 //0x00001360 is the device ID number for the PXI-6602
 //0x00002DB0 is the device ID number for the PCI-6608
 //0x00002CC0 is the device ID number for the PXI-6608

 bus = acquireBoard("PXI0::0::INSTR");

 if(bus == NULL){
  printf("Error accessing the PCI device.  Exiting.\n");
  return 1;
 }

 //Intitialise Mite Chip.

 initMite(bus);

 //Calling test function

 test(bus);


 releaseBoard(bus);
 return 0;
}

//Tell the MITE to link the  BAR1 address to the DAQ Board
//You must initialize the MITE before you write to the rest of the PCI board
void initMite(iBus *bus)
{
 tAddressSpace  Bar0;
 u32 physicalBar1;
 
 //Skip MITE initialization for PCMCIA boards
 //(which do not have a MITE DMA controller)
 if(!bus->get(kIsPciPxiBus,0)) return;
 
 Bar0 = bus->createAddressSpace(kPCI_BAR0);

 //Get the physical address of the DAQ board
 physicalBar1 = bus->get(kBusAddressPhysical,kPCI_BAR1);
 
 // ***** 6602/6608 specific MITE initialization *****
 // Hit the IO Window Base/Size Register 1 (IOWBSR1) in the MITE.  We set the
 // address, enable the window and set the size of the window:
 Bar0.write32(0xC4, (physicalBar1 & 0xffffff00L) | 0x8C);

 // Write to the IO Window Control Register 1 (IOWCR1) to make the IO window
 // go to RAM memory space instead of the config space
 Bar0.write32(0xF4, 0);
 
 // ***** End of 6602/6608 specific code *****
 
 bus->destroyAddressSpace(Bar0);
}


void test(iBus *bus)
{
 tAddressSpace  cardSpace;
 tTIO *board;
 
 cardSpace = bus->createAddressSpace(kPCI_BAR1);
 board = new tTIO(cardSpace);

 //Reset
 board->G01_Joint_Reset_Register.writeG0_Reset(1);

 //Disarm
 board->G0_Command_Register.writeG0_Disarm(1);

 //load initial value of 0
 board->G0_Load_A_Registers.writeG0_Load_A(0x00000000);
 board->G0_Command_Register.writeG0_Load(1);

 //set the counting mode to quadrature encoding X4
 board->G0_Counting_Mode_Register.setG0_Encoder_Counting_Mode(3);
 board->G0_Counting_Mode_Register.writeG0_Index_Phase(3);


 //set source to the internal timebase (20 MHz)
 board->G0_Input_Select_Register.writeG0_Source_Select(0);
 
 //set gate
 board->G0_Input_Select_Register.writeG0_Gate_Select(2);
 board->G0_Mode_Register.writeG0_Gate_Polarity(0);
 board->G0_Mode_Register.writeG0_Gating_Mode(2);
 board->G0_Mode_Register.writeG0_Trigger_Mode_For_Edge_Gate(3);
 
 //set counting direction to Gate IO connector
 board->G0_Command_Register.writeG0_Up_Down(2);

 //arm counter
 printf("counter value is 0x%08lx\n",
   board->G0_Save_Registers.readRegister());
 board->G0_Command_Register.writeG0_Arm(1);

 //delay long enough for the quadrature encoder to turn a few times
 {
  long int i,j;
  printf("waiting...\n");
  for(i=0;i<1000000;i++)
   for(j=0;j<3000;j++)
    ;
 }

 //read counter value
 //Use this method to read the value of an armed counter
 //during non-buffered counting.  Since the value of the counter may
 //change during the read, we make sure that the value is stable.
 unsigned long counterValue1, counterValue2;
 counterValue1 = board->G0_Save_Registers.readRegister();
 counterValue2 = board->G0_Save_Registers.readRegister();
 if(counterValue1 != counterValue2)
  counterValue1 = board->G0_Save_Registers.readRegister();
 printf("counter value is now 0x%08lx\n",counterValue1);

 //Disarm
 board->G0_Command_Register.writeG0_Disarm(1);
 
 delete board;
 bus->destroyAddressSpace(cardSpace);
}

 


 Other related files pls refer to attachments.

I have compiled my project under vs2005 successfully. when I run my application, the board which the application found is right. and the initMite is also right.

But I can not get the counter value. The counter value always remains 0xFFFFFFFF whatever I move the encoder. And the load value always remains 0xFFFFFFFF but not the value 0x0 which I have appointed.

I don't why?

And I also confused about this code:

//set source to the internal timebase (20 MHz)
 board->G0_Input_Select_Register.writeG0_Source_Select(0);
 

what's this code meanning? As in Register Level Programming Manual, this bit in Gi_Input_Select_Register selects the signal to serve as the source.

0---Timebase 1; the internal timebase(20M Hz)

2--Source Pin 0,I/O Pin 39;

 

which one do I have to select?

I have selected 0 and 2 to test my application, there is no change in result.

 

Need I select second gate? Is the second gate means signal B in my application?

How Can I configure my board?

And also I think my application need to use Gi_Save_Trace in Gi Command Register but the example not use, why ?

 

Thanks for all support!

Download All
0 Kudos
Message 1 of 7
(12,026 Views)

Hi Orient-

 

I see a few problems and I haven't had time to test yet, so these may help but also may not be all that is necessary to solve the problem:

 

  • You should use board->G0_Counting_Mode_Register.writeG0_Encoder_Counting_Mode(3); instead of board->G0_Counting_Mode_Register.setG0_Encoder_Counting_Mode(3);
  • According to the 660x RLP manual, in quadrature encoder mode "signal A connects to the dedicated source pin of the counter; signal B connects to the UP/DOWN pin; and signal Z connects to the GATE pin."

         so, you should change the source select to '2' for PFI 39 and the gate select to '2' for PFI 38.

 

Please give these a try and let us know your results.

Tom W
National Instruments
0 Kudos
Message 2 of 7
(12,008 Views)

Hi Tom W

 

Thanks for your response. I will update my code and test it again. But I also have some questions as blow:

1. Example code  using the code as below to init MITE

// ***** 6602/6608 specific MITE initialization *****
 // Hit the IO Window Base/Size Register 1 (IOWBSR1) in the MITE.  We set the
 // address, enable the window and set the size of the window:
 Bar0.write32(0xC4, (physicalBar1 & 0xffffff00L) | 0x8C);

 // Write to the IO Window Control Register 1 (IOWCR1) to make the IO window
 // go to RAM memory space instead of the config space
 Bar0.write32(0xF4, 0);
 
 // ***** End of 6602/6608 specific code *****

 

But this code is  6602/6608 specific code. my card is 6601. I used this code directly, it would make the card fail in MAX self-check. I changed this code as below

/ ***** 6602/6608 specific MITE initialization *****
 // Hit the IO Window Base/Size Register 1 (IOWBSR1) in the MITE.  We set the
 // address, enable the window and set the size of the window:
 Bar0.write32(0xC4+(u32)newBar0, ((u32)newBar0 & 0xffffff00) | 0x8C);
 printf ("newBar0: 0x%08X\n", newBar0);

 // Write to the IO Window Control Register 1 (IOWCR1) to make the IO window
 // go to RAM memory space instead of the config space
 Bar0.write32(0xF4+(u32)newBar0, 0);
 
 // ***** End of 6602/6608 specific code *****

 

In this code   newBar0=mappedMemory0; This code would not make the card fail in MAX self-check. But I also do not know this code is right or not. Pls to confirm.

2. What is the defference between write and set functions? for instance, in example code:

board->G0_Counting_Mode_Register.setG0_Encoder_Counting_Mode(3);  and

board->G0_Counting_Mode_Register.writeG0_Encoder_Counting_Mode(3);

 

I checked there defination, write function has more code flush(s); What's the  flush(s) meanning and its functions?

And the defference between read and get functions. How Can I choose the right function to use?

 

3. In my application, I will use the card to measure the encoder continuously, so I use armed counters to read counter values, refer to page 2-5 in RLP manual. In this reading mode, I have to trace the counter and latch the value in SW register. How Can I finish this function. I use the code as below, but It is seemed not right.

//delay long enough for the quadrature encoder to turn a few times//等一段时间,让编码器转过一个新角度
 /*{
  long int i,j;
  printf("waiting...\n");
  for(i=0;i<1000000;i++)
   for(j=0;j<3000;j++)
    ;
 }*/

 //temp16=board->G01_Status_Register.readG0_Counting_St();
 //printf("Counting status is %d\n",temp16);//This bit is asserted when the counter is armed and enabled for counting.

 //temp16=board->G01_Status_Register.readG0_Save_St();
 //printf("THe G0 save status is %d\n",temp16);//This bit indicates the state of the Gi SW Save Regester.It sets when the Gi SW Save Regester is latched for reading.

 //read counter value
 //Use this method to read the value of an armed counter
 //during non-buffered counting.  Since the value of the counter may
 //change during the read, we make sure that the value is stable.
 unsigned long counterValue0, counterValue0_z;

 board->G0_Command_Register.writeG0_Save_Trace(0);

  
 board->G0_Command_Register.writeG0_Save_Trace(1);
 
 //Gi_Save_St indicates the status of the Gi_SW_Register.It sets when the Gi_SW_Register is lached for reading.
 temp16=board->G01_Status_Register.getG0_Save_St();
 if(temp16)
 {
  counterValue0_z = board->G0_Save_Registers.readRegister();
  counterValue0 = board->G0_Save_Registers.readRegister();
  if(counterValue0 != counterValue0_z)
   counterValue0 = board->G0_Save_Registers.readRegister();
  if(runtimes==1000)
  {
   printf("counter0 value is now 0x%08lx\n",counterValue0);
   if(temp16==1)
    printf("Counter 0 Latching succeeds!!!!!");
   else
    printf("Counter 0 Latching process can not be completed!:0x%08lx\n\n",temp16);
   runtimes=0;
  }
 }

 

what's your comments about this code.

 

Thanks and regards.

 

Yao Jianyong

 

 

0 Kudos
Message 3 of 7
(12,004 Views)

Hi Yao Jianyong-

 

1.  The 6602/6608 initialization code is also correct for the 6601.  The required steps for initialization are shown on page 3-55 of the 660x RLP manual.  physicalBar1 should be the the value returned by this O/S call (as found in osiUserCode.cpp).  Your code does not configure the MITE correctly.  If you are using your device in RTX, then why do you need to test it in MAX?

 

deviceBar1.QuadPart = pciDeviceData->u.type0.BaseAddresses[1];

 

2.  The difference between write and set is that write sets soft copies of the register values and then flushes those values out to the bus.  Set() calls only set soft copy values but do not actually write to the registers on the device.  This is why you must use Flush() after making set calls.  The advantage of using Set()/Flush() is that you can set several bitfields within the same register and then flush them all at once, so you can avoid making multiple bus accesses if you know that you desire to update multiple bitfields within the same register. 

 

The same condition is true for read and get- read() goes out to the device and updates the soft copy value of the register or bitfield, and then returns the new value to you.  Get() only returns the soft copy.  So, if you need "fresh" data then you should always use Read().

 

3.  I am unclear on how you intend your code to function.  I would recommend restructuring it similarly to this:

 

bool latched = false

const int LOOP_LIMIT = 1000;

int i = 0;

while(!latched && i!=LOOP_LIMIT)

{

temp16 = board->G01_Status_Register.readG0_Save_St();

if(temp16)

{

latched =
true;

counterValue0_z = board->G0_Save_Registers.readRegister();

counterValue0 = board->G0_Save_Registers.readRegister();

if(counterValue0 != counterValue0_z)

counterValue0 = board->G0_Save_Registers.readRegister();

printf(
"counter0 value is now 0x%08lx\n",counterValue0);

}

// sleep here to throttle loop

i++;

}

printf("Counter 0 Latching process can not be completed!:0x%08lx\n\n",temp16);

 

 

 

 

Tom W
National Instruments
0 Kudos
Message 4 of 7
(11,987 Views)

Hi Tom W,

 

Thanks for your reply. I have make my application successful using the code as below:

void initMite(iBus *bus)
{
 tAddressSpace  Bar0;
 u32 physicalBar1;
 
 //Skip MITE initialization for PCMCIA boards
 //(which do not have a MITE DMA controller)
 if(!bus->get(kIsPciPxiBus,0)) return;//如果不返回,则说明成功了
 
 Bar0 = bus->createAddressSpace(kPCI_BAR0);

 //Get the physical address of the DAQ board
 physicalBar1 = bus->get(kBusAddressPhysical,kPCI_BAR1);

 printf ("physicalBar1: 0x%08X\n", physicalBar1); 
 
  /*
 // ***** 6602/6608 specific MITE initialization *****
 // Hit the IO Window Base/Size Register 1 (IOWBSR1) in the MITE.  We set the
 // address, enable the window and set the size of the window: 

 Bar0.write32(0xC4, ((u32)newBar1 & 0xffffff00L) | 0x8C);
 printf ("newBar1: 0x%08X\n", newBar1);

 // Write to the IO Window Control Register 1 (IOWCR1) to make the IO window
 // go to RAM memory space instead of the config space
 Bar0.write32(0xF4, 0x00);
 
 // ***** End of 6602/6608 specific code *****
 */ 
 bus->destroyAddressSpace(Bar0);
 printf("initMite success.\n");
}

void init(iBus *bus)

 cardSpace = bus->createAddressSpace(kPCI_BAR1);
 board = new tTIO(cardSpace);


 //Reset
 //Set this bit to 1 resets the counter.This bit automatically clears, and the counter disarmed.
 board->G01_Joint_Reset_Register.writeG0_Reset(1);

 //Disarm
 //Setting this bit diaarms the counter.
 board->G0_Command_Register.writeG0_Disarm(1);

 //load initial value of 0
 board->G0_Load_A_Registers.writeG0_Load_A(0x00000000);
 //board->G0_Load_B_Registers.writeG0_Load_B(0x00000000);
 board->G0_Command_Register.writeG0_Load(1);

 temp32 =board->G0_Load_A_Registers.readG0_Load_A();
 printf("The Load A value =0x%08X\n",temp32);

 //set the counting mode to quadrature encoding X4
 board->G0_Counting_Mode_Register.writeG0_Encoder_Counting_Mode(3);//

 board->G0_Counting_Mode_Register.writeG0_Index_Phase(3);

 //set source to the internal timebase (20 MHz)
 board->G0_Input_Select_Register.writeG0_Source_Select(0);
 

 //set gate
 board->G0_Input_Select_Register.writeG0_Gate_Select(2);//PFI37

 board->G0_Mode_Register.writeG0_Gate_Polarity(0);//

 board->G0_Mode_Register.writeG0_Gating_Mode(2);//

 board->G0_Mode_Register.writeG0_Trigger_Mode_For_Edge_Gate(3);//

 //set counting direction to Gate IO connector
 board->G0_Command_Register.writeG0_Up_Down(2);//

  //arm counter
 printf("counter value before arm is 0x%08lx\n",
   board->G0_Save_Registers.readRegister());


 board->G0_Command_Register.writeG0_Arm(1);

}

And also alter the code in osiBus.h as below:

/////////////////////////////////////////////////////////////////
// tAddressSpace
// Reads and Writes
//
// These look more complicated than they should to work around a known compiler
// bug in versions of gcc prior to 3.0.4. 

inline void tAddressSpace::write8(const u32 registerOffset, const u32 data)
{
   volatile u8* p = ((u8*) theSpace) + registerOffset;
  // printf("write data 0x%08X\n", data);
   //(void)(*p = (u8) data);
   *p = data;
   // printf("read from P memory 0x%08X\n", *p);
}

inline void tAddressSpace::write16( const u32 registerOffset, const u32 data)
{
   volatile u16* p = (u16*) (((u8*) theSpace) + registerOffset);
   //printf("write data 0x%08X\n", data);
   //(void)(*p = (u16) ReadLittleEndianU16(data));
   *p = data;
   //printf("read from P memory 0x%08X\n", *p);
}

inline void tAddressSpace::write32(const u32 registerOffset, const u32 data)
{
   volatile u32* p = (u32*) (((u8*) theSpace) + registerOffset);
   //printf("write data 0x%08X\n", data);
   //(void)(*p = ReadLittleEndianU32(data));
   *p = data;
  // printf("read from P memory 0x%08X\n", *p);
  
  
}

inline u8 tAddressSpace::read8(const u32 registerOffset)
{
   volatile u8* p = ((u8*) theSpace) + registerOffset;
   u8  data = *p;
   return data;
}

inline u16 tAddressSpace::read16(const u32 registerOffset)
{
   volatile u16* p = (u16*) (((u8*) theSpace) + registerOffset);
   u16  data = *p;
   //return ReadLittleEndianU16(data);
   return data;
}

inline u32 tAddressSpace::read32(const u32 registerOffset)
{
   volatile u32* p = (u32*) (((u8*) theSpace) + registerOffset);
   u32  data = *p;
   //return ReadLittleEndianU32(data);
    return data;
}

Now, I can get the right value of my encoder. But the index is not work.  The only problem is that my application cann't reload the value when Z signal is available. 

 

What is your suggestion about this problem?

0 Kudos
Message 5 of 7
(11,984 Views)

Hi Yao Jianyong-

 

You also need to enable the index mode by writing to writeG0_Index_Enable().  Once enabled, the counter should reload from the currently selected load register whenever the index condition (including phase of A and B) is met while the index signal is in the asserted value.

 

My suggestion would be to load your initial counter value to LoadA and load it into the counter.  Then, before arming the counter, load the desired reload value into LoadB, select LoadB as the load register (by writeG0_Load_Source_Select(1)) and then disable reload source switching.  That way, the value in LoadB will be reloaded to the counter each time the index condition is met.

Tom W
National Instruments
0 Kudos
Message 6 of 7
(11,979 Views)

Hi,

 

Is the above also true for RTX64 runing on windows 10 ?

 

Best Regards,

Efrat Gur

0 Kudos
Message 7 of 7
(4,966 Views)