unit MainUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls; type TMainForm = class(TForm) ScrollBox1: TScrollBox; ControlPanel: TPanel; Label1: TLabel; DiffCheck: TCheckBox; RangeCombo: TComboBox; CalLabel: TLabel; CalEdit: TComboBox; VoltPanel: TPanel; CountPanel: TPanel; SpacerBevel: TBevel; ADPollTime: TTimer; GoButton: TButton; procedure FormCreate(Sender: TObject); procedure RangeComboChange(Sender: TObject); procedure ADPollTimeTimer(Sender: TObject); procedure GoButtonClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.DFM} uses AIOUSB, DetectUnit; type TUSBAI16128Config = packed record ChannelRange: array[$0..$F] of Byte; CalibMode: Byte; TrigMode: Byte; StartStopCH: Byte; Oversample: Byte; AIMUXChEx: Byte; //AIMUX start and end address bit extension register. end; const Channels = 64; var DeviceIndex, NextChannel, NextSample, KnownBytesLeft: LongWord; Config: TUSBAI16128Config; ConfigBufSize: LongWord; VoltLabel: array of TLabel; CountGauge: array of TProgressBar; ADData: array[0..(128 * 1024 - 1)] of Word; //ADData: array[0..(128 * 4*1024 - 1)] of Word; //For the 128-channel board, 4K scans of 128 channels, or 8K scans of 64 differential channels. //For the 64-channel board, 8K scans of 64 channels, or 16K scans of 32 differential channels. procedure TMainForm.FormCreate(Sender: TObject); var Status, PID: LongWord; DetectForm: TDetectForm; Hz: Double; I: Integer; begin DeviceIndex := diOnly; //If you only have the one board, QueryDeviceInfo() with diOnly will succeed //with the right PID. If you have more than one board, or haven't plugged it //in, it will fail, and this code will pop up the detector form so the user //can pick a board or cancel. Status := QueryDeviceInfo(DeviceIndex, @PID, nil, nil, nil, nil); if (Status <> ERROR_SUCCESS) or not (((PID >= $8045) and (PID <= $8049)) or ((PID >= $8145) and (PID <= $8149))) then begin DetectForm := TDetectForm.Create(Self); if DetectForm.ShowModal = mrOK then begin DeviceIndex := DetectForm.DeviceIndex; DetectForm.Free; QueryDeviceInfo(DeviceIndex, @PID, nil, nil, nil, nil); end else begin DetectForm.Free; Application.Terminate; Exit; end; end; SetLength(VoltLabel, Channels); SetLength(CountGauge, Channels); SpacerBevel.Height := 17 * Channels + 2; for I := 0 to Channels-1 do begin VoltLabel[I] := TLabel.Create(Self); with VoltLabel[I] do begin AutoSize := False; Alignment := taCenter; Layout := tlCenter; Align := alTop; Top := 18 * I; Height := 17; Caption := 'Ch ' + IntToStr(I); Parent := VoltPanel; end; CountGauge[I] := TProgressBar.Create(Self); with CountGauge[I] do begin Min := 0; Max := $FFFF; Smooth := True; Align := alTop; Top := 18 * I; Height := 17; Parent := CountPanel; end; end; if ADC_QueryCal(DeviceIndex) <> ERROR_SUCCESS then begin //this board doesn't have calibration CalLabel.Visible := False; CalEdit.Visible := False; end; //Stop the counter, in case it was running. Hz := 0; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); //Set the combo box to the first A/D range, then call RangeComboChange() to //set all the channels to that range. RangeCombo.ItemIndex := 0; RangeComboChange(nil); Config.CalibMode := 0; //Take actual data, not internal calibration sources. Config.TrigMode := $05; //Scan selected channels each counter rising edge. Config.Oversample := 0; //No oversample. ConfigBufSize := SizeOf(Config); ADC_SetConfig(DeviceIndex, @Config, ConfigBufSize); NextChannel := 0; end; procedure TMainForm.RangeComboChange(Sender: TObject); var I: Integer; RangeCode: Byte; NewHeight: Integer; begin //The ranges in the combo box are listed in order, so that the index is equal //to the range code. RangeCode := RangeCombo.ItemIndex; if DiffCheck.Checked then RangeCode := RangeCode or $08; //Config.ChannelRange[0] controls channels $00 through $03; //Config.ChannelRange[1] controls channels $04 through $07; etc. //For simplicity, we set 'em all to the same range. for I := $0 to $F do Config.ChannelRange[I] := RangeCode ; if DiffCheck.Checked then NewHeight := 1 else NewHeight := 17; for I := Channels div 2 to Channels - 1 do begin VoltLabel[I].Height := NewHeight; CountGauge[I].Height := NewHeight; end; end; procedure TMainForm.GoButtonClick(Sender: TObject); var Status: LongWord; StartChannel, EndChannel: Byte; Hz: Double; begin StartChannel := 0; if DiffCheck.Checked then EndChannel := (Channels div 2) - 1 else EndChannel := Channels - 1 ; Config.StartStopCH := (EndChannel shl 4) or (StartChannel and $F); Config.AIMUXChEx := (EndChannel and $F0) or (StartChannel shr 4); ConfigBufSize := SizeOf(Config); ADC_SetConfig(DeviceIndex, @Config, ConfigBufSize); NextChannel := 0; CalEdit.Enabled := False; RangeCombo.Enabled := False; GoButton.Enabled := False; DiffCheck.Enabled := False; if CalEdit.Visible then begin Status := ADC_SetCal(DeviceIndex, PChar(CalEdit.Text)); if Status <> ERROR_SUCCESS then begin CalEdit.Enabled := True; RangeCombo.Enabled := True; GoButton.Enabled := True; DiffCheck.Enabled := True; Application.MessageBox(PChar('Calibration Error ' + IntToStr(Status)), 'Cal Error', MB_ICONEXCLAMATION); Exit; end; end; KnownBytesLeft := Length(ADData) * SizeOf(ADData[0]); ADC_BulkAcquire(DeviceIndex, KnownBytesLeft, @ADData[0]); NextSample := 0; //Give the worker thread time to get started on slower CPUs. Sleep(1); //Start the counter. Hz := 3000; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); ADPollTime.Enabled := True; end; procedure TMainForm.ADPollTimeTimer(Sender: TObject); var BytesLeft, Status, ChannelsUsed: LongWord; Volts: Double; RangeCode: Byte; Hz: Double; Counts: Word; begin Status := ADC_BulkPoll(DeviceIndex, BytesLeft); if Status <> ERROR_SUCCESS then begin Beep; Hz := 0; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); ADPollTime.Enabled := False; GoButton.Enabled := True; RangeCombo.Enabled := True; CalEdit.Enabled := True; DiffCheck.Enabled := True; Application.MessageBox(PChar('Error ' + IntToStr(Status) + ' from ADC_BulkPoll.'), 'ADC_BulkPoll Error', MB_ICONEXCLAMATION); Exit; end; if BytesLeft >= KnownBytesLeft then Exit; //If we get here, some data has been taken. if BytesLeft = 0 then begin //Stop the counter. Hz := 0; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); ADPollTime.Enabled := False; GoButton.Enabled := True; RangeCombo.Enabled := True; CalEdit.Enabled := True; DiffCheck.Enabled := True; end; if DiffCheck.Checked then ChannelsUsed := Channels div 2 else ChannelsUsed := Channels ; repeat Counts := ADData[NextSample]; CountGauge[NextChannel].Position := Counts; //We're going to use the range code to convert to volts. RangeCode := Config.ChannelRange[NextChannel shr 2]; Volts := Counts / $FFFF; //"Volts" now holds a value from 0 to 1. if (RangeCode and $01) <> 0 then begin //Bit 0(mask 01 hex) indicates bipolar. Volts := Volts * 2 - 1; //"Volts" now holds a value from -1 to +1. end; if (RangeCode and $02) = 0 then begin //Bit 1(mask 02 hex) indicates x2 gain. Volts := Volts * 2; end; if (RangeCode and $04) = 0 then begin //Bit 2(mask 04 hex) indicates x5 gain. Volts := Volts * 5; end; //"Volts" now holds volts. VoltLabel[NextChannel].Caption := Format('Ch %d: %.4f V', [ NextChannel, Volts ]); NextChannel := (NextChannel + 1) mod ChannelsUsed; Inc(NextSample); Dec(KnownBytesLeft, 2); until KnownBytesLeft = BytesLeft; end; end.