using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; // for the callback Marshaling using AIOUSBNet; // the namespace exposes the AIOUSB Class interface // You must also add a reference to the AIOUSBNet.dll in the project settings // (preferably from the system32 directory or equivelant) namespace Sample1 { public partial class Form1 : Form { // Callback definition etc is in the dll. // Declaration of callback function at Form Class level scope // so no Garbage collection will interfere with processing until the end of Form and App: // This could be on its own worker thread not in the UI thread (Form). public static AIOUSB.ADCallback myCallBack; // One and only Device Index: public UInt32 DeviceIndex = AIOUSB.diOnly; // AD specifics: bool bCal = true; public int numChannelsUsed = 16; // Num channels we will be using: public const int numAnalogInputs = 16; // Array of static controls for GUI updates used by callback for Asycronous BeginInvoke of delegates: public static ProgressBar[] VoltProgress = new ProgressBar[numAnalogInputs]; public static Label[] VoltLabel = new Label[numAnalogInputs]; public static CheckBox DiffCheckBox; public static ComboBox RangeComboBox; public static TextBox StatusTextBox; // Struct for AI Board Config: public struct TUSBAI1616Config { public TUSBAI1616Config(int size) { ChannelRange = new byte[size]; CalibMode = 0; TrigMode = 0; StartStopCH = 0; Oversample = 0; } public byte[] ChannelRange; public byte CalibMode; public byte TrigMode; public byte StartStopCH; public byte Oversample; } // Struct for Configuration must pass number of channels for array size: public TUSBAI1616Config Config = new TUSBAI1616Config(numAnalogInputs); // Required for ADC_SetConfig() dll calls: UInt32 ConfigBufSize = numAnalogInputs + 4; // sizeof(Config); // could be done cleaner // Temp array for ADC_SetConfig() dll calls: public byte[] configArray = new byte[20]; // Array for AD Data the counts are unsigned 16 bit numbers (ushort) public UInt16[] ADData = new UInt16[128 * 1024]; //8K scans of 16 channels, or 16K scans of 8 differential channels. public Form1() { InitializeComponent(); // Set the Arrays of controls: VoltProgress[0] = progressBar1; VoltProgress[1] = progressBar2; VoltProgress[2] = progressBar3; VoltProgress[3] = progressBar4; VoltProgress[4] = progressBar5; VoltProgress[5] = progressBar6; VoltProgress[6] = progressBar7; VoltProgress[7] = progressBar8; VoltProgress[8] = progressBar9; VoltProgress[9] = progressBar10; VoltProgress[10] = progressBar11; VoltProgress[11] = progressBar12; VoltProgress[12] = progressBar13; VoltProgress[13] = progressBar14; VoltProgress[14] = progressBar15; VoltProgress[15] = progressBar16; VoltLabel[0] = lblVolt0; VoltLabel[1] = lblVolt1; VoltLabel[2] = lblVolt2; VoltLabel[3] = lblVolt3; VoltLabel[4] = lblVolt4; VoltLabel[5] = lblVolt5; VoltLabel[6] = lblVolt6; VoltLabel[7] = lblVolt7; VoltLabel[8] = lblVolt8; VoltLabel[9] = lblVolt9; VoltLabel[10] = lblVolt10; VoltLabel[11] = lblVolt11; VoltLabel[12] = lblVolt12; VoltLabel[13] = lblVolt13; VoltLabel[14] = lblVolt14; VoltLabel[15] = lblVolt15; DiffCheckBox = checkBoxDIFF; RangeComboBox = comboBoxRange; StatusTextBox = textBoxStatus; } private void Form1_Load(object sender, EventArgs e) { // Called before Form is displayed Initialize resources: // Single instanciation here of callback function at Form Class level scope // so no Garbage collection will interfere with processing until the end of Form scope and App: myCallBack = new AIOUSB.ADCallback(ADCallbackReport); // Initialize default Device Only: DeviceIndex = AIOUSB.diOnly; // Device data: UInt32 Status; UInt32 PID = 0; UInt32 NameSize = 256; string strName = "name"; UInt32 DIOBytes = 0; UInt32 Counters = 0; //UInt64 SerNum; UInt32 ERROR_SUCCESS = 0; bool deviceIndexValid = false; // Get The Device Information test for valid device found AI and AIO boards: Status = AIOUSB.QueryDeviceInfo(DeviceIndex, out PID, out NameSize, out strName, out DIOBytes, out Counters); if ( (Status == ERROR_SUCCESS) && ( ( (PID >= 0x8040) && (PID <= 0x8044) ) || ( (PID >= 0x8140) && (PID <= 0x8144) ) ) )// AIO and AI { deviceIndexValid = true; } else { // If Only device is not valid then Launch connect device dialog box: // New parent aware subform: FormDetect DetectSubForm = new FormDetect(this); DetectSubForm.ShowDialog(); Status = AIOUSB.QueryDeviceInfo(DeviceIndex, out PID, out NameSize, out strName, out DIOBytes, out Counters); if (Status == ERROR_SUCCESS && PID >= 0x8040 && PID <= 0x815D) deviceIndexValid = true; } if (!deviceIndexValid) { // No valid device found should exit //this.Close(); } if (AIOUSB.ADC_QueryCal(DeviceIndex) != ERROR_SUCCESS) { // this board doesn't have calibration (so dont try and cal it) bCal = false; lblCal.Visible = false; comboBoxCalibration.Visible = false; } //Stop the counter, in case it was running. double Hz = 0; UInt32 BlockIndex = 0; AIOUSB.CTR_StartOutputFreq(DeviceIndex, BlockIndex, out Hz); comboBoxRange.SelectedIndex = 0; int i; byte RangeCode; RangeCode = (byte)comboBoxRange.SelectedIndex; if (checkBoxDIFF.Checked) RangeCode |= 0x08; for (i = 0; i < numAnalogInputs; i++) Config.ChannelRange[i] = RangeCode; // Temp copy struct to array for dll config call: for (i = 0; i < numAnalogInputs; i++) configArray[i] = Config.ChannelRange[i]; if (checkBoxDIFF.Checked) { numChannelsUsed = 8; configArray[18] = Config.StartStopCH = 0x70; //Select only 8 differential channels, from 0 to 7. } else { numChannelsUsed = 16; configArray[18] = Config.StartStopCH = 0xF0; //Select all 16 channels, from 0 to 15. } // Init all config values: Config.CalibMode = 0; //Take actual data, not internal calibration sources. Config.TrigMode = 5; //Scan selected channels each counter rising edge. Config.StartStopCH = 0xF0; //Scan selected channels. Config.Oversample = 0;//No oversample. //ConfigBufSize = 20; // Note This is set once above for ( i = 0; i < numAnalogInputs; i++) { Config.ChannelRange[i] = 1; } // Temp copy struct to array for dll call: for (i = 0; i < numAnalogInputs; i++) { configArray[i] = Config.ChannelRange[i]; } configArray[16] = Config.CalibMode; configArray[17] = Config.TrigMode; configArray[18] = Config.StartStopCH; configArray[19] = Config.Oversample; AIOUSB.ADC_SetConfig(DeviceIndex, configArray, out ConfigBufSize); for (i = 0; i < 16; i++) { VoltProgress[i].Value = 0; // SetPos(0); } // Set reasonable default values of choice: //comboBoxCalibration.SelectedItem = ":AUTO:"; //comboBoxCalibration.SelectedItem = ":NONE:"; comboBoxCalibration.SelectedItem = ":1TO1:"; comboBoxRange.SelectedItem = "±10 Volts"; } private void btnDevice_Click_1(object sender, EventArgs e) { // Launch connect device dialog box // Switch between multiple devices or reconnect // New parent aware subform: FormDetect DetectSubForm = new FormDetect(this); DetectSubForm.ShowDialog(); } private void comboBoxRange_SelectedIndexChanged(object sender, EventArgs e) { //The ranges in the combo box are listed in order, so that the index is equal //to the range code. int i; byte RangeCode; RangeCode = (byte)comboBoxRange.SelectedIndex; if (checkBoxDIFF.Checked) RangeCode |= 0x08; for (i = 0; i < numAnalogInputs; i++) Config.ChannelRange[i] = RangeCode; // Temp copy struct to array for dll config call: for (i = 0; i < numAnalogInputs; i++) configArray[i] = Config.ChannelRange[i]; if (checkBoxDIFF.Checked) { numChannelsUsed = 8; configArray[18] = Config.StartStopCH = 0x70; //Select all 8 differential channels, from 0 to 7. } else { numChannelsUsed = 16; configArray[18] = Config.StartStopCH = 0xF0; //Select all 16 channels, from 0 to 15. } } private void checkBoxDIFF_CheckedChanged(object sender, EventArgs e) { comboBoxRange_SelectedIndexChanged(sender, e); } private void btnExecute_Click(object sender, EventArgs e) { UInt32 Status = 1; int ERROR_SUCCESS = 0; textBoxStatus.Text = "Acquiring..."; //Stop the counter, in case it was running. double Hz = 0; AIOUSB.CTR_StartOutputFreq(DeviceIndex, 0, out Hz); // Array was set above Status = AIOUSB.ADC_SetConfig(DeviceIndex, configArray, out ConfigBufSize); // If cal is avail: if (bCal) { Status = AIOUSB.ADC_SetCal(DeviceIndex, comboBoxCalibration.SelectedItem.ToString()); if (Status != 0) //ERROR_SUCCESS ) { MessageBox.Show("Calibration Error!"); return; } } //Since we've put it in scan mode, this is 1kHz per channel, or 16kHz total. Hz = 1000; AIOUSB.CTR_StartOutputFreq(DeviceIndex, 0, out Hz); // Register the callback and start acqusition: // Note: 1024 bytes requested so 512 Uint16 elements should be returned Status = AIOUSB.ADC_BulkContinuousCallbackStart(DeviceIndex, 1024, 64, 0, myCallBack); if(Status != ERROR_SUCCESS) textBoxStatus.Text = "Error " + Status.ToString("00") + " starting acquisition."; //Exit; comboBoxCalibration.Enabled = false; comboBoxRange.Enabled = false; btnExecute.Enabled = false; // prevent unintentional multiple callbacks otherwise may need serialization etc btnStop.Enabled = true; checkBoxDIFF.Enabled = false; } // Must use threadsafe delegate across threads for GUI update: private delegate void UpdateTextDel(int i, UInt16 Counts); // Updates the GUI Text: private void UpdateTextDisplay(int i, UInt16 Counts) { // Converting counts to volts: // We will need to safely get the settings used from the Form UI controls: byte RangeCode; int ChannelsUsed = 16; double Volts = 0; //We're going to use the range code to convert to volts. RangeCode = (byte)RangeComboBox.SelectedIndex; if (DiffCheckBox.Checked) ChannelsUsed = ChannelsUsed / 2; // local copy Volts = (double)(Counts) / (double)(0xFFFF); //"Volts" now holds a value from 0 to 1. if ((RangeCode & 0x01) != 0) //Bit 0(mask 01 hex) indicates bipolar. { Volts = Volts * 2 - 1; //"Volts" now holds a value from -1 to +1. } if ((RangeCode & 0x02) == 0) //Bit 1(mask 02 hex) indicates x2 gain. { Volts *= 2; } if ((RangeCode & 0x04) == 0) //Bit 2(mask 04 hex) indicates x5 gain. { Volts *= 5; } // Set the GUI text data. string strVolt; string strFormat = "Ch {0,2:D2}: {1,8:F3} V"; strVolt = String.Format(strFormat, i, Volts); if (i < ChannelsUsed) { VoltLabel[i].Text = strVolt; } } // Must use threadsafe delegate across threads for GUI update: private delegate void UpdateProgressDel(int i, UInt16 Counts); // Updates the GUI Progress control: private void UpdateProgressDisplay(int i, UInt16 Counts) { // Converting counts to volts: // We will need to safely get the settings used form the Form UI controls: byte RangeCode; int ChannelsUsed = 16; double Volts; //We're going to use the range code to convert to volts. RangeCode = (byte)RangeComboBox.SelectedIndex; RangeCode = 1; if (DiffCheckBox.Checked) ChannelsUsed = ChannelsUsed / 2; // local copy Volts = (double)(Counts) / (double)(0xFFFF); //"Volts" now holds a value from 0 to 1. if ((RangeCode & 0x01) != 0) //Bit 0(mask 01 hex) indicates bipolar. { Volts = Volts * 2 - 1; //"Volts" now holds a value from -1 to +1. } if ((RangeCode & 0x02) == 0) //Bit 1(mask 02 hex) indicates x2 gain. { Volts *= 2; } if ((RangeCode & 0x04) == 0) //Bit 2(mask 04 hex) indicates x5 gain. { Volts *= 5; } // Set the GUI progress data; if (i < ChannelsUsed) { VoltProgress[i].Value = 10000 + (int)(1000 * Volts); } } // Must use threadsafe delegate across threads for GUI update: private delegate void UpdateStatusDel(string strText); // Updates the GUI Text: private void UpdateStatusDisplay(string strText) { textBoxStatus.Text += strText; } // Method that matches delegate signature to recieve data buffer: // Note: BuffSize returned is actually number of bytes so array of UInt16's lenghth should actually be 1/2 BuffSize public void ADCallbackReport(IntPtr pBuf, UInt32 BufSize, UInt32 Flags, UInt32 Context) { // This flag (2) tells us we've got the last continuous callback packet: if (((Flags & 2) != 0)) { String strEnd = "\r\nStream ended..."; // This may be getting blcoked by stop button func on UI thread? StatusTextBox.BeginInvoke(new UpdateStatusDel(UpdateStatusDisplay), new object[] { strEnd }); System.Threading.Thread.Sleep(100); // let last ui update occur since were on UI thread } //Note: This is called from an alternate thread context. //(Specifically a worker thread spawned by the driver.) // Recieved data and send to Form control delegates to convert counts to voltage and update GUI: // Safely copy pBuff into an Int16 array then convert to UInt16 // This is required due to CLR restraints on IntPtr and Marshal.Copy() for unmanaged code array pointers int BuffLen = ((int)BufSize / 2) ; // proper length of the buffer in 16 bit elements based on retuned number of 8 bit bytes Int16[] array = new Int16[BuffLen]; // array for buff Int16 copy UInt16[] ChannelCounts = new UInt16[BuffLen]; // buffer for all final UInt16 count data if (BuffLen != 0) // if no data to copy { Marshal.Copy(pBuf, array, 0, (array.Length)); // copy for (int i = 0; i < BuffLen; ++i) { ChannelCounts[i] = (UInt16)array[i]; // convert Int16 to UInt16 (could use BitConverter Class) } } //Because the display can be slow, and this is called from a worker //thread, we just load each channel's last reading for display. //Any direct processing of the data, like a Fourier transform, would go //here instead. if ( (BufSize >= 32) ) // if enough data to display { // Display new data: int Channels = 16; for (int i = 0; i < Channels; ++i) { // Must be run asynchronously on the UI thread (BeginInvoke vs. Invoke) (members of the ISynchronizeInvoke interface) // ...asynchronous version of Invoke, which returns immediately and arranges for the method to run on the UI thread at some point in the future // Similar to but not the same as Asyncronous delegate Invocation to prevent thread blocking or deadlock VoltLabel[i].BeginInvoke(new UpdateTextDel(UpdateTextDisplay), new object[] { i, ChannelCounts[i] }); VoltProgress[i].BeginInvoke(new UpdateProgressDel(UpdateProgressDisplay), new object[] { i, ChannelCounts[i] }); } } } private void btnStop_Click(object sender, EventArgs e) { // Stop the acquisition verify return values: UInt32 EndStatus, IOStatus = 1; UInt32 ERROR_SUCCESS = 0; // Stop Continuous acquisition: EndStatus = AIOUSB.ADC_BulkContinuousEnd(DeviceIndex, out IOStatus); //Make sure we got the "stream ended" message first. Application.DoEvents(); // For BeginInvoke UI update System.Threading.Thread.Sleep(100); // Since were on this UI thread if (EndStatus == ERROR_SUCCESS && IOStatus == ERROR_SUCCESS) textBoxStatus.Text += "\r\nAcquisition complete."; else if (EndStatus == ERROR_SUCCESS) textBoxStatus.Text = "Acquisition ended with I/O error " + IOStatus.ToString("00") + "."; else textBoxStatus.Text = "Error " + EndStatus.ToString("00") + " ending acquisition."; comboBoxCalibration.Enabled = true; comboBoxRange.Enabled = true; btnExecute.Enabled = true; btnStop.Enabled = false; checkBoxDIFF.Enabled = true; } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { UInt32 EndStatus, IOStatus = 1; // Stop Continuous acquisition: EndStatus = AIOUSB.ADC_BulkContinuousEnd(DeviceIndex, out IOStatus); } } }