unit MainUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ExtCtrls; const AM_GOTDATA = WM_USER + $99; AM_ENDSTREAM = WM_USER + 98; type TMainForm = class(TForm) RangeCombo: TComboBox; Label1: TLabel; GoButton: TButton; CountTrack0: TTrackBar; VoltLabel0: TLabel; CalLabel: TLabel; CountTrack1: TTrackBar; VoltLabel1: TLabel; CountTrack2: TTrackBar; VoltLabel2: TLabel; CountTrack3: TTrackBar; VoltLabel3: TLabel; CountTrack4: TTrackBar; VoltLabel4: TLabel; CountTrack5: TTrackBar; VoltLabel5: TLabel; CountTrack6: TTrackBar; VoltLabel6: TLabel; CountTrack7: TTrackBar; VoltLabel7: TLabel; CountTrack8: TTrackBar; VoltLabel8: TLabel; CountTrack9: TTrackBar; VoltLabel9: TLabel; CountTrackA: TTrackBar; VoltLabelA: TLabel; CountTrackB: TTrackBar; VoltLabelB: TLabel; CountTrackC: TTrackBar; VoltLabelC: TLabel; CountTrackD: TTrackBar; VoltLabelD: TLabel; CountTrackE: TTrackBar; VoltLabelE: TLabel; CountTrackF: TTrackBar; VoltLabelF: TLabel; DiffCheck: TCheckBox; CalCombo: TComboBox; StopButton: TButton; ReportLabel: TLabel; procedure FormCreate(Sender: TObject); procedure RangeComboChange(Sender: TObject); procedure GoButtonClick(Sender: TObject); procedure StopButtonClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); protected procedure AMGotData(var Msg: TMessage); message AM_GOTDATA; procedure AMEndStream(var Msg: TMessage); message AM_ENDSTREAM; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.DFM} uses AIOUSB, DetectUnit; type TUSBAI1616Config = packed record ChannelRange: array[$0..$F] of Byte; CalibMode: Byte; TrigMode: Byte; StartStopCh: Byte; Oversample: Byte; StartStopChEx: Byte; //added for compatibility with the AI-MUX family end; var DeviceIndex: LongWord; Config: TUSBAI1616Config; NextChannel, NumChannels: Integer; ChannelVolts: array of Double; VoltLabel: array[$0..$F] of TLabel; CountTrack: array[$0..$F] of TTrackBar; procedure ADCallback(pBuf: PWord; BufSize, Flags, Context: LongWord); cdecl; forward; procedure TMainForm.FormCreate(Sender: TObject); var DetectForm: TDetectForm; Status, PID, ConfigBufSize: LongWord; 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 >= $8040) and (PID <= $8044)) or ((PID >= $8140) and (PID <= $8144))) then begin DetectForm := TDetectForm.Create(Self); if DetectForm.ShowModal = mrOK then begin DeviceIndex := DetectForm.DeviceIndex; DetectForm.Free; end else begin DeviceIndex := diNone; DetectForm.Free; Application.Terminate; Exit; end; end; VoltLabel[$0] := VoltLabel0; VoltLabel[$1] := VoltLabel1; VoltLabel[$2] := VoltLabel2; VoltLabel[$3] := VoltLabel3; VoltLabel[$4] := VoltLabel4; VoltLabel[$5] := VoltLabel5; VoltLabel[$6] := VoltLabel6; VoltLabel[$7] := VoltLabel7; VoltLabel[$8] := VoltLabel8; VoltLabel[$9] := VoltLabel9; VoltLabel[$A] := VoltLabelA; VoltLabel[$B] := VoltLabelB; VoltLabel[$C] := VoltLabelC; VoltLabel[$D] := VoltLabelD; VoltLabel[$E] := VoltLabelE; VoltLabel[$F] := VoltLabelF; CountTrack[$0] := CountTrack0; CountTrack[$1] := CountTrack1; CountTrack[$2] := CountTrack2; CountTrack[$3] := CountTrack3; CountTrack[$4] := CountTrack4; CountTrack[$5] := CountTrack5; CountTrack[$6] := CountTrack6; CountTrack[$7] := CountTrack7; CountTrack[$8] := CountTrack8; CountTrack[$9] := CountTrack9; CountTrack[$A] := CountTrackA; CountTrack[$B] := CountTrackB; CountTrack[$C] := CountTrackC; CountTrack[$D] := CountTrackD; CountTrack[$E] := CountTrackE; CountTrack[$F] := CountTrackF; if ADC_QueryCal(DeviceIndex) <> ERROR_SUCCESS then begin //this board doesn't have calibration CalLabel.Visible := False; CalCombo.Visible := False; end; //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); end; procedure TMainForm.RangeComboChange(Sender: TObject); var I: Integer; RangeCode: Byte; bNewVisible: Boolean; 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; for I := $0 to $F do Config.ChannelRange[I] := RangeCode ; if DiffCheck.Checked then begin NumChannels := 8; Config.StartStopCH := $70; //Select all 8 differential channels, from 0 to 7. end else begin NumChannels := 16; Config.StartStopCH := $F0; //Select all 16 channels, from 0 to 15. end; for I := $0 to $F do begin if (Config.ChannelRange[I and $F] and 1) = 0 then CountTrack[I].Min := 0 else CountTrack[I].Min := -1000 ; CountTrack[I].Max := 1000; if (Config.ChannelRange[I and $F] and 2) = 0 then begin CountTrack[I].Min := CountTrack[I].Min * 2; CountTrack[I].Max := CountTrack[I].Max * 2; end; if (Config.ChannelRange[I and $F] and 4) = 0 then begin CountTrack[I].Min := CountTrack[I].Min * 5; CountTrack[I].Max := CountTrack[I].Max * 5; end; CountTrack[I].Position := I; end; bNewVisible := not DiffCheck.Checked; for I := 8 to 15 do begin VoltLabel[I].Visible := bNewVisible; CountTrack[I].Visible := bNewVisible; end; end; procedure TMainForm.GoButtonClick(Sender: TObject); var Status: LongWord; ConfigBufSize: LongWord; Hz: Double; begin ReportLabel.Caption := 'Acquiring...'; ADC_BulkMode(DeviceIndex, $1); //Stop the counter, in case it was running. Hz := 0; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); ConfigBufSize := SizeOf(Config); ADC_SetConfig(DeviceIndex, @Config, ConfigBufSize); if CalCombo.Visible then begin Status := ADC_SetCal(DeviceIndex, PChar(CalCombo.Text)); if Status <> ERROR_SUCCESS then begin ReportLabel.Caption := 'Calibration Error ' + IntToStr(Status); Exit; end; end; SetLength(ChannelVolts, 16); NextChannel := 0; //Since we've put it in scan mode, this is 1kHz per channel, or 16kHz total. Hz := 1000; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); Status := ADC_BulkContinuousCallbackStart(DeviceIndex, 1024, 64, 0, @ADCallback); if Status <> ERROR_SUCCESS then begin ReportLabel.Caption := 'Error ' + IntToStr(Status) + ' starting acquisition.'; Exit; end; CalCombo.Enabled := False; RangeCombo.Enabled := False; GoButton.Enabled := False; StopButton.Enabled := True; DiffCheck.Enabled := False; end; procedure ADCallback(pBuf: PWord; BufSize, Flags, Context: LongWord); cdecl; var Volts: Double; iSample: Integer; Range: Byte; begin //Note: This is called from an alternate thread context. //(Specifically a worker thread spawned by the driver.) for iSample := 0 to (BufSize div 2) - 1 do begin Volts := 1/65536 * pBuf^; Inc(pBuf); Range := Config.ChannelRange[NextChannel]; if (Range and 1) <> 0 then Volts := Volts * 2 - 1; if (Range and 2) = 0 then Volts := Volts * 2; if (Range and 4) = 0 then Volts := Volts * 5; //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. ChannelVolts[NextChannel] := Volts; Inc(NextChannel); if NextChannel >= NumChannels then NextChannel := 0; end; //This tells the main thread new data is ready. PostMessage(MainForm.Handle, AM_GOTDATA, 0, 0); //This tells the main thread we've got the last continuous callback packet. if (Flags and 2) <> 0 then PostMessage(MainForm.Handle, AM_ENDSTREAM, 0, 0); end; procedure TMainForm.AMGotData(var Msg: TMessage); var iChannel: Integer; begin for iChannel := 0 to $F do begin VoltLabel[iChannel].Caption := 'Ch ' + IntToStr(iChannel) + ': ' + Format('%.3f', [ ChannelVolts[iChannel] ]); CountTrack[iChannel].Position := Round(1000 * ChannelVolts[iChannel]); end; end; procedure TMainForm.AMEndStream(var Msg: TMessage); begin ReportLabel.Caption := 'Stream ended...'; end; procedure TMainForm.StopButtonClick(Sender: TObject); var EndStatus, IOStatus: LongWord; begin EndStatus := ADC_BulkContinuousEnd(DeviceIndex, @IOStatus); Application.ProcessMessages; //Make sure we got the "stream ended" message first. if (EndStatus = ERROR_SUCCESS) and (IOStatus = ERROR_SUCCESS) then ReportLabel.Caption := 'Acquisition complete.' else if EndStatus = ERROR_SUCCESS then ReportLabel.Caption := 'Acquisition ended with I/O error ' + IntToStr(IOStatus) + '.' else ReportLabel.Caption := 'Error ' + IntToStr(EndStatus) + ' ending acquisition.' ; GoButton.Enabled := True; StopButton.Enabled := False; RangeCombo.Enabled := True; CalCombo.Enabled := True; DiffCheck.Enabled := True; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin if DeviceIndex <> diNone then ADC_BulkMode(DeviceIndex, $1); end; end.