Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

6363 MHDDK Analog Read

Solved!
Go to solution

Hi, I'm porting some QNX C++ code from a 6259 card to a 6363, using the MHDDK, and I've got digital in and out, and analog out working fine, but the analog input is proving tricky! 

 

The code below configures the card and reads 4 channels into the FIFO, and it correctly gets 4 readings in the FIFO, but they're all about 0V, not the 5V input signal I'm putting in.

 

It's almost like the multiplexer isn't actually switching the inputs, or something like that, any help would be much appreciated!

 

 

// 6363 Configuration

// Reset data mode to a known default software baseline
p6363Multi->AI.AI_Config_FIFO_Data_Register.setRegister(0, &status);
p6363Multi->AI.AI_Data_Mode_Register.writeRegister(0x00000000, &status);
p6363Multi->AI.AI_Trigger_Select_Register.setRegister(0, &status);
p6363Multi->AI.AI_Trigger_Select_Register2.setRegister(0, &status);
p6363Multi->AI.AI_Timer.Reset_Register.writeReset(1, &status);

// Setting the above reset bit has the side effect of modifying the
// following registers. Therefore set the soft copy as well. Note that
// AI/DI registers may be touched as well. See their respective helpers!
p6363Multi->AI.AI_Timer.Mode_2_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SI_Load_A_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SI_Load_B_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SC_Load_A_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SC_Load_B_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SI2_Load_A_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.SI2_Load_B_Register.setRegister(0, &status);
p6363Multi->AI.AI_Timer.DIV_Load_A_Register.setRegister(0, &status);

// Disable Interrupts, strobe all acknowledges.
p6363Multi->AI.AI_Timer.Interrupt2_Register.writeRegister(0xFFFFFFFF, &status);
// The following clears the FITO
p6363Multi->AI.AI_Timer.Reset_Register.writeFIFO_Clear(1, &status);

p6363Multi->AI.AI_Timer.Reset_Register.writeConfiguration_Start(kTrue, &status);

// Program the external gate
p6363Multi->AI.AI_Trigger_Select_Register.setAI_External_Gate_Select(nAI::kGate_Disabled, &status);
p6363Multi->AI.AI_Trigger_Select_Register.setAI_External_Gate_Polarity(nAI::kActive_High_Or_Rising_Edge, &status);
p6363Multi->AI.AI_Trigger_Select_Register.flush(&status);

// Auto-trigger the START1 signal (start trigger)
p6363Multi->AI.AI_Trigger_Select_Register.setAI_START1_Select(nAI::kStart1_Low, &status);
p6363Multi->AI.AI_Trigger_Select_Register.setAI_START1_Polarity(nAI::kActive_Low_Or_Falling_Edge, &status);
p6363Multi->AI.AI_Trigger_Select_Register.setAI_START1_Edge(kFalse, &status); // ...level (not falling edge)
p6363Multi->AI.AI_Trigger_Select_Register.flush(&status);

p6363Multi->AI.AI_Trigger_Select_Register2.setAI_START_Select(nAI::kStartCnv_Low, &status);
p6363Multi->AI.AI_Trigger_Select_Register2.setAI_START_Edge(kTrue, &status);
p6363Multi->AI.AI_Trigger_Select_Register2.setAI_START_Polarity(nAI::kActive_High_Or_Rising_Edge, &status);
p6363Multi->AI.AI_Trigger_Select_Register2.flush(&status);

// Program the convert clock to start on the sample clock
p6363Multi->AI.AI_Trigger_Select_Register.setAI_CONVERT_Source_Select(nAI::kStartCnv_InternalTiming, &status);
p6363Multi->AI.AI_Trigger_Select_Register.setAI_Convert_Source_Polarity(nAI::kActive_Low_Or_Falling_Edge, &status);
p6363Multi->AI.AI_Trigger_Select_Register.flush(&status);

// Program the sample and convert clock timing specifications
p6363Multi->AI.AI_Timer.Mode_1_Register.setStart_Stop_Gate_Enable(nInTimer::kDisabled, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.setTrigger_Once(kTrue, &status); // be able to re-trigger
p6363Multi->AI.AI_Timer.Mode_1_Register.setContinuous(0, &status); // acquire a predetermined number of scans.
p6363Multi->AI.AI_Timer.Mode_1_Register.setPre_Trigger(nInTimer::kPretrigger, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.setSC_Initial_Load_Source(nInTimer::kLoad_B, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.setSC_Reload_Mode(nInTimer::kSC_Reload_Switch, &status); // kSC_Reload_Switch
p6363Multi->AI.AI_Timer.SC_Load_B_Register.writeSC_Load_B(0, &status);

int numberOfChannels = 4;
p6363Multi->AI.AI_Timer.SC_Load_A_Register.writeSC_Load_A(numberOfChannels, &status);
p6363Multi->AI.AI_Timer.Command_Register.writeSC_Load(1, &status);

p6363Multi->AI.AI_Timer.Mode_2_Register.setStart1_Export_Mode(nInTimer::kExportSynchronizedStart1, &status);
p6363Multi->AI.AI_Timer.Mode_2_Register.setStart2_Export_Mode(nInTimer::kExportUnmaskedStart2, &status);
p6363Multi->AI.AI_Timer.Mode_2_Register.setStart_Trigger_Length(nInTimer::kExportSynchronizedStart, &status);
p6363Multi->AI.AI_Timer.Mode_2_Register.setSyncMode(nInTimer::kSyncDefault, &status); // default
p6363Multi->AI.AI_Timer.Mode_2_Register.setHaltOnError(1, &status);
p6363Multi->AI.AI_Timer.Mode_2_Register.flush(&status);

// Select TB3 (100 MHz internal timebase) as the source for the SI counter
p6363Multi->AI.AI_Timer.Mode_1_Register.writeSI_Source_Select(nInTimer::kSI_Src_TB3, &status);

// Configure SI initial load and alternate behavior (standard for finite/continuous scanning)
p6363Multi->AI.AI_Timer.Mode_1_Register.setSI_Initial_Load_Source(nInTimer::kLoad_A, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.setSI_Reload_Mode(nInTimer::kSI_Reload_Alt_First_Period_Every_STOP, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.flush(&status);

// Load the SI period (Sample Interval)
// E.g., 100,000 ticks of 100MHz = 1ms between scans.
// Even for 1 sample per channel total, the state machine requires a valid terminal count.
p6363Multi->AI.AI_Timer.SI_Load_A_Register.writeSI_Load_A(99999, &status);
p6363Multi->AI.AI_Timer.Command_Register.writeSI_Load(1, &status);

p6363Multi->AI.AI_Timer.Mode_1_Register.setSI2_Initial_Load_Source(nInTimer::kLoad_A, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.setSI2_Reload_Mode(nInTimer::kSI2_Reload_Alt_First_Period_Every_STOP, &status);
p6363Multi->AI.AI_Timer.Mode_1_Register.flush(&status);

p6363Multi->AI.AI_Timer.Mode_2_Register.writeSI2_Source_Select(nInTimer::kSI2_Src_IsTB3, &status);
// Load the convert to sample clock delay
p6363Multi->AI.AI_Timer.SI2_Load_A_Register.writeSI2_Load_A(399, &status); // 2
p6363Multi->AI.AI_Timer.Command_Register.writeSI2_Load(1, &status);
// Load the convert period
p6363Multi->AI.AI_Timer.SI2_Load_B_Register.writeSI2_Load_B(399, &status); // 400
p6363Multi->AI.AI_Timer.Mode_1_Register.writeSI2_Initial_Load_Source(nInTimer::kLoad_A, &status);

p6363Multi->AI.AI_Data_Mode_Register.writeAI_FifoWidth(nAI::kTwoByteFifo, &status);

// Clear configuration FIFO
p6363Multi->AI.AI_Timer.Reset_Register.writeConfiguration_Memory_Clear(1, &status);

for ( u16 i = 0; i<numberOfChannels; i++)
{
// Set channel parameters
if ( i == (numberOfChannels-1)) {
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Last_Channel(1, &status);
} else {
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Last_Channel(0, &status);
}

p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Dither(nAI::kEnabled, &status); // Dithering helps increase ADC accuracy
// gain 0=5V range, 1=10V range, 2=2V range, 3=1V range
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Gain(2, &status); // Set the gain (used by the hardware)
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Channel_Type(nAI::kRSE, &status); // Single-ended: aiN vs aiGnd
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Bank(nAI::kBank0, &status); // AI channels 0..15 are on bank0
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Channel(i, &status); // aiN where N = i

p6363Multi->AI.AI_Config_FIFO_Data_Register.flush(&status);

// Advance the configuration FIFO
p6363Multi->AI.AI_Timer.Command_Register.writeLOCALMUX_CLK_Pulse(1, &status);
}

// Leave timing configuration mode
p6363Multi->AI.AI_Timer.Reset_Register.writeConfiguration_End(kTrue, &status);

// Arm the AI subsystem
// Arm bits must be set at the same time since this is a strobe register
u32 commandValue = 0;

// Determine what to arm
// commandValue |= (1 << 0x2); // SI Arm (Sample Clock Engine)
commandValue |= (1 << 0x6); // SC Arm (Scan Counter)
commandValue |= (1 << 0xc); // SI2 Arm (Convert Clock Engine)
commandValue |= (1 << 0x8); // DIV Arm (Divider)

p6363Multi->AI.AI_Timer.Command_Register.writeRegister(commandValue, &status);

// Poll the armed state, with timeout
int runTime = 500;
while (!p6363Multi->AI.AI_Timer.Status_1_Register.readSC_Armed_St(&status) && status.isNotFatal())
{
if (runTime-- == 0)
{
printf("Error: Timing engine did not arm.\n");
status.setCode(kStatusRLPTimeout);
break;
}
delay(1);
}

 
// 6363 Reading
 
// The START1 signal (start trigger) auto-triggers
p6363Multi->AI.AI_Timer.Command_Register.writeSTART_Pulse(1, &status);

// wait for it to complete
int loopCount = 1000;
while (p6363Multi->AI.AI_Timer.Status_1_Register.readScan_In_Progress_St(&status) && (loopCount-- > 0))
{
// Spin on the Scan In Progress bit
delay(1);
}

u32 samplesAvailable = p6363Multi->AI.AI_Data_FIFO_Status_Register.readRegister(&status);
int channelCount = 4;
int rawData[channelCount];

if (samplesAvailable >= channelCount)
{
// Read from the 16-bit FIFO data register since it was configured as such
for (u32 m=0; m<channelCount; m++)
{
rawData[m] = p6363Multi->AI.AI_FIFO_Data_Register16.readRegister();
std::cout << "AI Channel " << m << " = " << rawData[m] << std::endl;
}
} else {
std::cout << samplesAvailable << " samples available!" << std::endl;
}
0 Kudos
Message 1 of 2
(384 Views)
Solution
Accepted by topic author alex-b

Hi, so I've resolved the issue!

 

Basically, the line that reads

// gain 0=5V range, 1=10V range, 2=2V range, 3=1V range
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Gain(2&status); 

 

is completely misleading, and should be.

// gain settings of PGA
// 0 = disconnected
// 1 = x1  (-10V to +10V range)
// 2 = x2  ( -5V to +5V range)
// 3 = x5  ( -2V to +2V range)
// 4 = x10 ( -1V to +1V range)
p6363Multi->AI.AI_Config_FIFO_Data_Register.setAI_Config_Gain(2&status); 
 

 

That gets me working ADC readings, and hopefully anyone else who comes across this post will be saved some time and effort in the future too!

 

Good luck all!

 

Alex

0 Kudos
Message 2 of 2
(161 Views)