unit MainUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ExtCtrls; type TMainForm = class(TForm) ConfigPanel: TPanel; CalLabel: TLabel; RangeLabel: TLabel; CalCombo: TComboBox; RangeCombo: TComboBox; DiffCheck: TCheckBox; GoButton: TButton; PollTime: TTimer; MainScroll: TScrollBox; LogMemo: TMemo; Splitter1: TSplitter; procedure FormCreate(Sender: TObject); procedure GoButtonClick(Sender: TObject); procedure PollTimeTimer(Sender: TObject); protected procedure DoBuildDisplay; private { Private declarations } 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; HighChannels: Byte; //added for compatibility with the AI-MUX family end; var DeviceIndex: LongWord; Config: TUSBAI1616Config; ADData: array of Word; NextSample, NextChannel: Integer; KnownBytesLeft: LongWord; ChannelPanel: array[0..15] of TPanel; VoltLabel: array[0..15] of TLabel; CountProgress: array[0..15] of TProgressBar; ChannelVolts: array[0..15] of Double; procedure TMainForm.FormCreate(Sender: TObject); var DetectForm: TDetectForm; Status, PID: LongWord; begin DoBuildDisplay; 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. //If you're only using one board in the final environment, you can error out //if diOnly doesn't work; if you're using multiple boards, it's probably best //to use GetDeviceByEEPROMByte() or the like. 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 DetectForm.Free; Application.Terminate; Exit; end; end; if ADC_QueryCal(DeviceIndex) <> ERROR_SUCCESS then begin //this board doesn't have calibration CalLabel.Visible := False; CalCombo.Visible := False; end; end; procedure TMainForm.DoBuildDisplay; var I, W: Integer; begin // W := MainScroll.Width; for I := 0 to 15 do begin ChannelPanel[I] := TPanel.Create(Self); ChannelPanel[I].BevelOuter := bvNone; ChannelPanel[I].Anchors := [akLeft, akTop, akRight]; ChannelPanel[I].SetBounds(0, I * 24, W, 24); ChannelPanel[I].Parent := MainScroll; VoltLabel[I] := TLabel.Create(Self); VoltLabel[I].AutoSize := False; VoltLabel[I].Layout := tlCenter; VoltLabel[I].Caption := 'Ch' + IntToStr(I); VoltLabel[I].SetBounds(8, 4, 80, 16); VoltLabel[I].Parent := ChannelPanel[I]; CountProgress[I] := TProgressBar.Create(Self); CountProgress[I].Smooth := True; CountProgress[I].Min := -10000; CountProgress[I].Max := +10000; CountProgress[I].Anchors := [akLeft, akTop, akRight]; CountProgress[I].SetBounds(88, 4, W - 88 - 8, 16); CountProgress[I].Parent := ChannelPanel[I]; end; //Set the range combo box to the first A/D range. RangeCombo.ItemIndex := 0; end; procedure TMainForm.GoButtonClick(Sender: TObject); var Status: LongWord; ConfigBufSize: LongWord; I: Integer; RangeCode: Byte; bDifferential: Boolean; Hz: Double; begin LogMemo.Lines.Clear; LogMemo.Repaint; if CalCombo.Visible then begin Status := ADC_SetCal(DeviceIndex, PChar(CalCombo.Text)); if Status <> ERROR_SUCCESS then begin LogMemo.Lines.Add('Error ' + IntToStr(Status) + ' setting calibration.'); LogMemo.Lines.Add(''); Exit; end; end; //Stop the counter, in case it was running. Hz := 0; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); bDifferential := DiffCheck.Checked; //The ranges in the combo box are listed in order, so that the index is equal to the range code. RangeCode := RangeCombo.ItemIndex; if bDifferential then RangeCode := RangeCode or $08; for I := $0 to $F do Config.ChannelRange[I] := RangeCode ; if bDifferential then Config.StartStopCH := $70 //Select all differential channels, 0 to 7. else Config.StartStopCH := $F0 //Select all channels, 0 to 15. ; 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); if bDifferential then for I := 15 downto 8 do begin ChannelPanel[I].Visible := False; end else for I := 8 to 15 do begin ChannelPanel[I].Visible := True; end ; //Since we've put it in scan mode above, this is 1kHz per channel, or 16kHz total. Hz := 1000; CTR_StartOutputFreq(DeviceIndex, 0, @Hz); //Set the streaming block size fairly small, to make the UI more responsive. //The default (large) streaming block size is necessary for a slow computer, //or when acquiring close to the max for a fast board. AIOUSB_SetStreamingBlockSize(DeviceIndex, 512); SetLength(ADData, 128*1024); KnownBytesLeft := Length(ADData) * SizeOf(ADData[0]); Status := ADC_BulkAcquire(DeviceIndex, KnownBytesLeft, @ADData[0]); if Status <> ERROR_SUCCESS then begin LogMemo.Lines.Add('Error ' + IntToStr(Status) + ' starting acquisition.'); LogMemo.Lines.Add(''); Exit; end; LogMemo.Lines.Add('Acquiring...'); LogMemo.Lines.Add(''); NextSample := 0; NextChannel := 0; CalCombo.Enabled := False; RangeCombo.Enabled := False; DiffCheck.Enabled := False; GoButton.Enabled := False; PollTime.Enabled := True; end; procedure TMainForm.PollTimeTimer(Sender: TObject); var Status: LongWord; BytesLeft: LongWord; Volts: Double; Range: Byte; I: Integer; begin if not PollTime.Enabled then Exit; //In case we have an extra WM_TIMER in the queue when turning the timer off. Status := ADC_BulkPoll(DeviceIndex, BytesLeft); if Status <> ERROR_SUCCESS then begin LogMemo.Lines.Add('Error ' + IntToStr(Status) + ' from ADC_BulkPoll.'); LogMemo.Lines.Add(''); CalCombo.Enabled := True; RangeCombo.Enabled := True; DiffCheck.Enabled := True; GoButton.Enabled := True; PollTime.Enabled := False; Exit; end; if BytesLeft >= KnownBytesLeft then Exit; //No new data. //If we get here, at least some new data has been taken. if BytesLeft = 0 then begin LogMemo.Lines.Add('Acquisition complete.'); LogMemo.Lines.Add(''); CalCombo.Enabled := True; RangeCombo.Enabled := True; DiffCheck.Enabled := True; GoButton.Enabled := True; PollTime.Enabled := False; end; repeat Volts := 1/65536 * ADData[NextSample]; 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, we just display the last reading for each channel. ChannelVolts[NextChannel] := Volts; Dec(KnownBytesLeft, 2); Inc(NextSample); Inc(NextChannel); if NextChannel > $F then NextChannel := 0; until KnownBytesLeft = BytesLeft; for I := 0 to $F do begin VoltLabel[I].Caption := 'Ch ' + IntToStr(I) + ': ' + Format('%.3f', [ ChannelVolts[I] ]); CountProgress[I].Position := Round(1000 * ChannelVolts[I]); end; end; end.