unit MainUnit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ImgList, ExtCtrls, Buttons, ComCtrls; type TMainForm = class(TForm) LevelImages: TImageList; ReadTime: TTimer; DataMemo: TMemo; DataMemoLabel: TLabel; Panel1: TPanel; InstructionsMemo: TMemo; SendCardButton: TButton; ReceiveCardButton: TButton; DataMemoFillButton: TButton; ClearDataMemoButton: TButton; SendButton: TButton; ReceiveButton: TButton; ClockPanel: TPanel; ClockLabel: TLabel; ClockTrack: TTrackBar; Panel3: TPanel; Label3: TLabel; Label4: TLabel; Label5: TLabel; Label2: TLabel; Image1: TImage; Image2: TImage; Image3: TImage; Image4: TImage; Image5: TImage; Image6: TImage; Image7: TImage; Image8: TImage; Image9: TImage; Image10: TImage; Image11: TImage; Image12: TImage; Image17: TImage; Image18: TImage; Image19: TImage; Image25: TImage; Image26: TImage; Image27: TImage; OutButtonA: TSpeedButton; OutButtonB: TSpeedButton; OutButtonC: TSpeedButton; OutButtonD: TSpeedButton; BitLabelA7: TLabel; BitLabelA6: TLabel; BitLabelA5: TLabel; BitLabelA4: TLabel; BitLabelA3: TLabel; BitLabelA2: TLabel; BitLabelA1: TLabel; BitLabelA0: TLabel; BitLabelB3: TLabel; BitLabelB2: TLabel; BitLabelB1: TLabel; BitLabelB0: TLabel; BitLabelC2: TLabel; BitLabelC1: TLabel; BitLabelC0: TLabel; BitLabelD2: TLabel; BitLabelD1: TLabel; BitLabelD0: TLabel; OutButtonFast: TSpeedButton; Label6: TLabel; TristateAButton: TSpeedButton; TristateOtherButton: TSpeedButton; InstructionsLabel: TLabel; RecLenEdit: TEdit; InHexCheck: TCheckBox; procedure FormCreate(Sender: TObject); procedure ReadTimeTimer(Sender: TObject); procedure LevelImageClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure OutButtonClick(Sender: TObject); procedure TristateButtonClick(Sender: TObject); procedure SendCardButtonClick(Sender: TObject); procedure ReceiveCardButtonClick(Sender: TObject); procedure ClearDataMemoButtonClick(Sender: TObject); procedure DataMemoFillButtonClick(Sender: TObject); procedure SendButtonClick(Sender: TObject); procedure DataMemoChange(Sender: TObject); procedure ReceiveButtonClick(Sender: TObject); procedure ClockTrackChange(Sender: TObject); procedure InHexCheckClick(Sender: TObject); protected procedure DoConfig; public end; var MainForm: TMainForm; implementation {$R *.DFM} uses AIOUSB, DetectUnit; var DeviceIndex: LongWord; LevelImage: array[0..31] of TImage; //Only some are actually stuffed, the others will be nil OutButton: array[0..3] of TSpeedButton; TristateButton: array[0..1] of TSpeedButton; LowLevel, HighLevel: TBitmap; DIOData: LongWord; DoUpdate: Boolean; FastMode: (fmDI, fmDO, fmDIO); procedure DumpStringToFile(S, Path: String); var Fil: TFileStream; begin Fil := TFileStream.Create(Path, fmCreate or fmShareDenyWrite); try Fil.Write(S[1], Length(S)); finally Fil.Free; end; end; function DumpFileToString(Path: String): String; var Fil: TFileStream; L: Integer; begin Fil := TFileStream.Create(Path, fmOpenRead or fmShareDenyNone); try L := Fil.Size; SetString(Result, nil, L); Fil.Read(Result[1], L); finally Fil.Free; end; end; procedure TMainForm.FormCreate(Sender: TObject); var DetectForm: TDetectForm; Status, PID: LongWord; OutMask, TristateMask: Byte; I: Integer; ReadClockHz, WriteClockHz: Double; begin LowLevel := TBitmap.Create; HighLevel := TBitmap.Create; DeviceIndex := diOnly; Status := QueryDeviceInfo(DeviceIndex, @PID, nil, nil, nil, nil); if (Status <> ERROR_SUCCESS) or ((PID and $FFF8) <> $8008) 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; LevelImage[ 0] := Image1; LevelImage[ 1] := Image2; LevelImage[ 2] := Image3; LevelImage[ 3] := Image4; LevelImage[ 4] := Image5; LevelImage[ 5] := Image6; LevelImage[ 6] := Image7; LevelImage[ 7] := Image8; LevelImage[ 8] := Image9; LevelImage[ 9] := Image10; LevelImage[10] := Image11; LevelImage[11] := Image12; LevelImage[16] := Image17; LevelImage[17] := Image18; LevelImage[18] := Image19; LevelImage[24] := Image25; LevelImage[25] := Image26; LevelImage[26] := Image27; OutButton[0] := OutButtonA; OutButton[1] := OutButtonB; OutButton[2] := OutButtonC; OutButton[3] := OutButtonD; TristateButton[0] := TristateAButton; TristateButton[1] := TristateOtherButton; InstructionsLabel.Font.Style := [fsBold]; SendButton.Font.Style := [fsBold]; ReceiveButton.Font.Style := [fsBold]; LevelImages.GetBitmap(0, LowLevel); LevelImages.GetBitmap(1, HighLevel); DIO_ConfigurationQuery(DeviceIndex, @OutMask, @TristateMask); DIO_ReadAll(DeviceIndex, @DIOData); for I := 0 to 2 do OutButton[I].Down := (OutMask and (1 shl I)) <> 0; OutButton[3].Down := not OutButton[2].Down; for I := 0 to 1 do TristateButton[I].Down := (TristateMask and (1 shl I)) <> 0; if (PID = $8008) or (PID = $800A) or (PID = $800D) then begin //Input-only boards FastMode := fmDI; OutButtonFast.Down := False; ReceiveCardButton.Visible := True; SendCardButton.Visible := False; end else if (PID = $8009) or (PID = $800E) then begin //Output-only boards FastMode := fmDO; OutButtonFast.Down := True; ReceiveCardButton.Visible := False; SendCardButton.Visible := True; end else begin //Input/output boards FastMode := fmDIO; OutButtonFast.Down := True; ReceiveCardButton.Visible := True; SendCardButton.Visible := True; end; if OutButtonFast.Down then begin ClockTrack.Position := 3; ClockTrack.OnChange(ClockTrack); ReceiveButton.Visible := False; SendButton.Visible := True; DataMemoFillButton.Visible := True; DataMemoChange(DataMemo); RecLenEdit.Visible := False; end else begin ClockTrack.Position := 0; ClockTrack.OnChange(ClockTrack); ReceiveButton.Visible := True; SendButton.Visible := False; DataMemoFillButton.Visible := False; DataMemoChange(DataMemo); RecLenEdit.Visible := True; end; ReadClockHz := 0; //External or "off" WriteClockHz := 0; //External or "off" DIO_StreamSetClocks(DeviceIndex, ReadClockHz, WriteClockHz); DoConfig; ReadTime.Enabled := True; end; procedure TMainForm.FormDestroy(Sender: TObject); begin LowLevel.Free; HighLevel.Free; end; procedure TMainForm.ReadTimeTimer(Sender: TObject); var I: Integer; oDIOData, Changes: LongWord; begin oDIOData := DIOData; DIO_ReadAll(DeviceIndex, @DIOData); Changes := DIOData xor oDIOData; if DoUpdate then begin Changes := $FFFFFFFF; DoUpdate := False; end; for I := 0 to 31 do if LevelImage[I] <> nil then if (Changes and (1 shl I)) <> 0 then begin if (DIOData and (1 shl I)) <> 0 then LevelImage[I].Picture.Assign(HighLevel) else LevelImage[I].Picture.Assign(LowLevel) end ; end; procedure TMainForm.LevelImageClick(Sender: TObject); var I: Integer; NewValue: Boolean; begin I := (Sender as TImage).Tag; DIOData := DIOData xor (1 shl I); //Toggle bit NewValue := (DIOData and (1 shl I)) <> 0; //Get new bit value DIO_Write1(DeviceIndex, I, NewValue); //Write it to the board DoUpdate := True; end; procedure TMainForm.DoConfig; var I: Integer; OutMask, TristateMask: Byte; begin OutMask := $00; for I := 0 to 3 do if OutButton[I].Down then OutMask := OutMask or (1 shl I) ; TristateMask := $00; for I := 0 to 1 do if TristateButton[I].Down then TristateMask := TristateMask or (1 shl I) ; DIO_ConfigureEx(DeviceIndex, @OutMask, @DIOData, @TristateMask); DIO_StreamOpen(DeviceIndex, not OutButtonFast.Down); DIO_StreamClose(DeviceIndex); //AIOUSB_ClearFIFO(DeviceIndex, 0); //Clear newly selected FIFO DoUpdate := True; end; procedure TMainForm.OutButtonClick(Sender: TObject); var I: Integer; begin //C and D always have opposite directions, so if they change one, change the other to be opposite if Sender = OutButton[2] then OutButton[3].Down := not OutButton[2].Down else if Sender = OutButton[3] then OutButton[2].Down := not OutButton[3].Down else if Sender = OutButtonFast then begin //If the board only has one mode, then only allow that one mode case FastMode of fmDI: OutButtonFast.Down := False; fmDO: OutButtonFast.Down := True; fmDIO: ; end; //Show the buttons for the current direction ReceiveButton.Visible := not OutButtonFast.Down; SendButton.Visible := OutButtonFast.Down; DataMemoFillButton.Visible := OutButtonFast.Down; DataMemoChange(DataMemo); RecLenEdit.Visible := not OutButtonFast.Down; end; I := (Sender as TSpeedButton).Tag; if I < 4 then DIOData := DIOData or ($FF shl (I * 8)) //Set new output byte high, in case user made a mistake; ignored for inputs ; DoConfig; end; procedure TMainForm.TristateButtonClick(Sender: TObject); begin DoConfig; end; procedure TMainForm.SendCardButtonClick(Sender: TObject); begin if FastMode = fmDI then Exit; OutButton[0].Down := True; OutButton[1].Down := True; OutButton[2].Down := True; OutButton[3].Down := False; OutButtonFast.Down := True; OutButtonFast.OnClick(OutButtonFast); TristateButton[0].Down := False; TristateButton[1].Down := False; DoConfig; ClockTrack.Position := 3; ClockTrack.OnChange(ClockTrack); end; procedure TMainForm.ReceiveCardButtonClick(Sender: TObject); begin if FastMode = fmDO then Exit; OutButton[0].Down := False; OutButton[1].Down := False; OutButton[2].Down := False; OutButton[3].Down := True; OutButtonFast.Down := False; OutButtonFast.OnClick(OutButtonFast); TristateButton[0].Down := False; TristateButton[1].Down := False; DoConfig; ClockTrack.Position := 0; ClockTrack.OnChange(ClockTrack); end; procedure TMainForm.ClearDataMemoButtonClick(Sender: TObject); begin DataMemo.Lines.Clear; DataMemo.OnChange(DataMemo); end; procedure TMainForm.DataMemoFillButtonClick(Sender: TObject); var Buf: String; begin Buf := DumpFileToString('sample.txt'); //You could also programmatically generate a stream of data, rather than loading it from a file. //If you want to send more flexible data, get this data string directly in SendButtonClick() - a memo will alter non-text characters. //However, this sample is designed to work with text, so here we put it in the memo, so a user can edit it before sending it. DataMemo.Lines.Text := Buf; end; function NybbleToHex(N: Byte): Char; begin N := N and $F; if N < 10 then Result := Char(Byte('0') + N) else Result := Char(Byte('A') - 10 + N) ; end; function HexToNybble(Hex: Char): Byte; begin case Hex of '0'..'9': Result := Byte(Hex) - Byte('0'); 'A'..'F': Result := Byte(Hex) - Byte('A') + 10; 'a'..'f': Result := Byte(Hex) - Byte('a') + 10; else raise Exception.CreateFmt('Invalid hex character "%s"', [ Hex ]); end; end; function StrToHex(Text: String): String; var I: Integer; begin SetString(Result, nil, Length(Text) * 2); for I := 1 to Length(Text) do begin Result[I * 2 - 1] := NybbleToHex(Byte(Text[I]) shr 4); Result[I * 2] := NybbleToHex(Byte(Text[I]) ); end; end; function HexToStr(Hex: String): String; var I: Integer; begin SetString(Result, nil, (Length(Hex) + 1) div 2); for I := Length(Result) downto 1 do Result[I] := Char(HexToNybble(Hex[I * 2]) + HexToNybble(Hex[I * 2 - 1]) shl 4) ; end; procedure TMainForm.SendButtonClick(Sender: TObject); var ReadClockHz, WriteClockHz: Double; Buf: String; BytesTransferred: LongWord; begin ReadClockHz := 0; //External or "off" WriteClockHz := ClockTrack.Position * 1000; DIO_StreamSetClocks(DeviceIndex, ReadClockHz, WriteClockHz); //Get the data to send; here we get the data from the memo, but see DataMemoFillButtonClick() for other options. Buf := DataMemo.Lines.Text; if InHexCheck.Checked then Buf := HexToStr(Buf); if (Length(Buf) and 1) <> 0 then Buf := Buf + #0; //Pad out the data to whole words, since the board sends a word at a time DIO_StreamOpen(DeviceIndex, False); DIO_StreamFrame(DeviceIndex, Length(Buf) div 2, @Buf[1], BytesTransferred); DIO_StreamClose(DeviceIndex); end; procedure TMainForm.DataMemoChange(Sender: TObject); begin if OutButtonFast.Down then DataMemoLabel.Caption := 'Data To Send: ' + IntToStr((DataMemo.GetTextLen + 1) div 2) + ' Words' else DataMemoLabel.Caption := 'Data To Receive:' ; end; procedure TMainForm.ReceiveButtonClick(Sender: TObject); var ReadClockHz, WriteClockHz: Double; Buf: String; ReadSize, BytesTransferred: LongWord; begin ReadClockHz := ClockTrack.Position * 1000; WriteClockHz := 0; //External or "off" DIO_StreamSetClocks(DeviceIndex, ReadClockHz, WriteClockHz); ReadSize := StrToInt(Trim(RecLenEdit.Text)); SetString(Buf, nil, ReadSize * 2); DIO_StreamOpen(DeviceIndex, True); DIO_StreamFrame(DeviceIndex, ReadSize, @Buf[1], BytesTransferred); DIO_StreamClose(DeviceIndex); SetLength(Buf, BytesTransferred); //Rather than displaying the received data in a memo, you could save it, process it further, etc. if InHexCheck.Checked then Buf := StrToHex(Buf); DataMemo.Lines.Text := Buf; DataMemoLabel.Caption := 'Data Received: ' + IntToStr(BytesTransferred div 2) + ' Words'; end; procedure TMainForm.ClockTrackChange(Sender: TObject); var Speed: Integer; begin Speed := ClockTrack.Position; if Speed = 0 then ClockLabel.Caption := 'external clock' else ClockLabel.Caption := IntToStr(Speed) + ' KHz' ; end; procedure TMainForm.InHexCheckClick(Sender: TObject); begin DataMemo.Lines.Clear; end; end.