// 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;
}