AKI-232C を Android で使う (2018/04/01)

秋月電子のRS232Cラインモニタキット AKI-232C (リンクはこちら)用のモニタツールの Android 版です。

 ※ラインモニタキットに付属しているモニタツールは、Windows 2000 / XP 用ですが、
 こちらの環境では、Windows 8.1 (64bit) でも動いています。

・OTG (USB-Host) に対応した Android 端末でのみ使用できます。
・タブレット端末 Nexus 7 での動作を確認しています。
・7 インチを基準にしているため、スマホ端末では、画面全体が縮小されます。
・通信速度が速い場合、データの取りこぼしが発生する場合があります。
・データ表示はリアルタイムではありません。1024バイト以上受信、または [STOP] ボタンをタップ後に表示されます。

■ダウンロード
 APK本体のみ(ダウンロードは、こちら

 ※USB - RS232C 通信には、CSD 社の Uni232C コンポーネントを使用しています。(リンクはこちら
  AKI-232C 添付ツールのソースコードを参考にしています。



■ソースコード
 Delphi 10.2 Tokyo + Uni23C コンポーネント


unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts,
  Uni232C, FMX.Edit, FMX.Controls.Presentation, FMX.StdCtrls, FMX.ListBox,
  FMX.ScrollBox, FMX.Memo, System.Rtti, FMX.Grid.Style, FMX.Grid, System.UIConsts,
  Androidapi.JNIBridge, AndroidApi.JNI.Media,
  System.IOUtils, System.IniFiles;

type

  TRxData = record
    Tick : Byte;
    Data : Byte;
  end;

  TComReadThread = class(TThread)
  private
    { Private 宣言 }
    procedure ComRead;
  protected
    procedure Execute; override;
  public
    constructor Create; virtual;
  end;

  TForm2 = class(TForm)
    ScaledLayout1: TScaledLayout;
    Uni232C1: TUni232C;
    Button1: TButton;
    ComboBox1: TComboBox;
    ComboBox2: TComboBox;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    StringGrid1: TStringGrid;
    StringColumn1: TStringColumn;
    StringColumn2: TStringColumn;
    StringColumn3: TStringColumn;
    StringColumn4: TStringColumn;
    StringColumn5: TStringColumn;
    StringColumn6: TStringColumn;
    StringColumn7: TStringColumn;
    StringColumn8: TStringColumn;
    StringColumn9: TStringColumn;
    StringColumn10: TStringColumn;
    StringColumn11: TStringColumn;
    StringColumn12: TStringColumn;
    StringColumn13: TStringColumn;
    StringColumn14: TStringColumn;
    StringColumn15: TStringColumn;
    StringColumn16: TStringColumn;
    StringColumn17: TStringColumn;
    StringColumn18: TStringColumn;
    StringColumn19: TStringColumn;
    StringColumn20: TStringColumn;
    StringColumn21: TStringColumn;
    StringColumn22: TStringColumn;
    StringColumn23: TStringColumn;
    StringColumn24: TStringColumn;
    StringColumn25: TStringColumn;
    StringColumn26: TStringColumn;
    StringColumn27: TStringColumn;
    StringColumn28: TStringColumn;
    StringColumn29: TStringColumn;
    StringColumn30: TStringColumn;
    StringColumn31: TStringColumn;
    StringColumn32: TStringColumn;
    StringColumn33: TStringColumn;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    GroupBox1: TGroupBox;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure StringGrid1DrawColumnCell(Sender: TObject; const Canvas: TCanvas;
      const Column: TColumn; const Bounds: TRectF; const Row: Integer;
      const Value: TValue; const State: TGridDrawStates);
    procedure Button5Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure CheckBox1Change(Sender: TObject);
    procedure CheckBox2Change(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { private 宣言 }
  public
    { public 宣言 }
    procedure DispRxData;
  end;

var
  Form2: TForm2;
  // モニタバイト数の2倍
  RxDataAry : array [0..2047] of TRxData;
  RxDataIndex : integer;
  GTicks : Cardinal;

  Gloopflag : boolean;
  GStartIndex{, GEndIndex} : integer;
  GCmdMode : integer;

  ThComRead : TComReadThread;

  ControlCode : array of string = [
    'NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL', 'BS',  'HT',
    'LF',  'VT',  'FF',  'CR',  'SO',  'SI',  'DLE', 'DC1', 'DC2', 'DC3',
    'DC4', 'NAK', 'SYN', 'ETB', 'CAN', 'EM',  'SUB', 'ESC',  'FS', 'GS',
    'RS',  'US', 'DEL']; //DEL = $7E

implementation

{$R *.fmx}

procedure Beep(typ : integer);
// ブザー音
// uses ... Androidapi.JNIBridge, AndroidApi.JNI.Media;
var
  ToneGenerator: JToneGenerator;
  toneType : integer;
begin

  ToneGenerator := TJToneGenerator.JavaClass.init(
    TJAudioManager.JavaClass.STREAM_ALARM,
    TJToneGenerator.JavaClass.MAX_VOLUME);

  //https://developer.android.com/reference/android/media/ToneGenerator.html

  if typ = 1 then
    toneType := TJToneGenerator.JavaClass.TONE_PROP_BEEP
  else if typ = 2 then
    toneType := TJToneGenerator.JavaClass.TONE_PROP_BEEP2
  else
    toneType := TJToneGenerator.JavaClass.TONE_PROP_ACK;

  ToneGenerator.startTone(toneType);//, 1000);
end;

function CheckRxData:string;
var
  i, k: integer;
  temp : array [0 ..2047] of TRxData;//Byte;
  a, b : Byte;
  ngcnt : integer;
  res : string;
begin
  res := '';
  for i := 0 to 2047 do begin
    temp[i].Data := 0;
    temp[i].Tick := 0;
  end;
  i := 0;
  k := 0;
  ngcnt := 0;
  while true do begin
    a := RxDataAry[i].Data shr 4;
    b := RxDataAry[i + 1].Data shr 4;
    if ((a = $8) and (b = $9)) or  ((a = $A) and (b = $B)) then begin
      temp[k] := RxDataAry[i];
      temp[k + 1] := RxDataAry[i + 1];
      k := k + 2;
      i := i + 2;
    end
    else begin
      temp[k] := RxDataAry[i];
      temp[k + 1] := RxDataAry[i];
      k := k + 2;
      Inc(i);
      Inc(ngcnt);
      res := res + ((k + 1) div 2).ToString + ',';
    end;
    if k > 2047 then break;
    if i > 2047 then break;
    if i >= RxDataIndex then break;
  end;
  for i := 0 to 2047 do RxDataAry[i] := temp[i];
  if ngcnt > 0 then
    result := 'NG = '+ ngcnt.ToString + ' : '+ Copy(res, 1, res.Length -1);
end;

function RateMesure: string;
// ボーレート計測
var
  DataCount : integer;
  Baud : array [0..99] of integer;
  m, n : integer;
  BaudRate : integer;
  bps : string;
begin
  result := '計測失敗';
  if RxDataIndex > 100 then begin
    DataCount := RxDataIndex;
    m := 0;
    for n := 0 to DataCount div 2 - 1 do begin
      Baud[m] := RxDataAry[n * 2 + 1].Data;
      Baud[m] := Baud[m] * 256;
      Baud[m] := Baud[m] + RxDataAry[n * 2].Data + 15;
      Baud[m] := Trunc(Baud[m] * 108.459); // nsec 108.459=1/9.22MHz
      Inc(m);
      if m = 100 then Break;
    end;
    BaudRate := 5000000; // nsec
    for n := 0 to 50 -1 do begin
      if Baud[n] <> 0  then begin
        if Baud[n] < BaudRate then BaudRate := Baud[n];
      end;
    end;

    if      (3333332 * 0.85 < BaudRate) and (BaudRate < 3333332 * 1.15) then bps :=   '300bps'
    else if (1666666 * 0.85 < BaudRate) and (BaudRate < 1666666 * 1.15) then bps :=   '600bps'
    else if ( 833333 * 0.85 < BaudRate) and (BaudRate <  833333 * 1.15) then bps :=   '1200bps'
    else if ( 416666 * 0.85 < BaudRate) and (BaudRate <  416666 * 1.15) then bps :=   '2400bps'
    else if ( 208332 * 0.85 < BaudRate) and (BaudRate <  208332 * 1.15) then bps :=   '4800bps'
    else if ( 104166 * 0.85 < BaudRate) and (BaudRate <  104166 * 1.15) then bps :=   '9600bps'
    else if (  69444 * 0.85 < BaudRate) and (BaudRate <   69444 * 1.15) then bps :=  '14400bps'
    else if (  52083 * 0.85 < BaudRate) and (BaudRate <   52083 * 1.15) then bps :=  '19200bps'
    else if (  34722 * 0.85 < BaudRate) and (BaudRate <   34722 * 1.15) then bps :=   '28800ps'
    else if (  26041 * 0.85 < BaudRate) and (BaudRate <   26041 * 1.15) then bps :=  '38400bps'
    else if (  17361 * 0.85 < BaudRate) and (BaudRate <   17361 * 1.15) then bps :=  '57600bps'
    else if (   8680 * 0.85 < BaudRate) and (BaudRate <    8680 * 1.15) then bps := '115200bps'
    else if (   4340 * 0.85 < BaudRate) and (BaudRate <    4340 * 1.15) then bps := '230400bps'
    else if (   2170 * 0.85 < BaudRate) and (BaudRate <    2170 * 1.15) then bps := '460800bps'
    else if (   1085 * 0.85 < BaudRate) and (BaudRate <    1085 * 1.15) then bps := '921600bps';
    result := bps;
  end;
end;

procedure TForm2.DispRxData;
var
  i, j: integer;
  a, b, c, d: Byte;
  cnt : integer;
  nth, idx, k : integer;
begin
  if RxDataIndex >= 2 then begin
    idx := 0;
    cnt := 0;
    nth := 0;
    with StringGrid1 do begin
      BeginUpDate;
      for i := GStartIndex div 2 to RxDataIndex div 2 - 1 do begin
        k := nth * 6;

        a := RxDataAry[i * 2].Data shl 4;
        b := RxDataAry[i * 2 + 1].Data and $0F;

        c := RxDataAry[i * 2].Data shr 4;
        d := RxDataAry[i * 2 + 1].Data shr 4;
        if (c = $A) and (d = $B) then begin
          Cells[1 + idx, k + 2] := IntToHex(a + b, 2);
          Cells[1 + idx, k + 3] := Char(a + b);
          if Cells[1 + idx, k + 4] <> '' then Cells[1 + idx, k + 4] := '';
          if Cells[1 + idx, k + 5] <> '' then Cells[1 + idx, k + 5] := '';
        end
        else if (c = 8) and (d = 9) then begin
          if Cells[1 + idx, k + 2] <> '' then Cells[1 + idx, k + 2] := '';
          if Cells[1 + idx, k + 3] <> '' then Cells[1 + idx, k + 3] := '';
          Cells[1 + idx, k + 4] := IntToHex(a + b, 2);
          Cells[1 + idx, k + 5] := Char(a + b);
        end
        else begin
          for j := 2 to 5 do
            if Cells[1 + idx, k + j] <> '' then Cells[1 + idx, k + j] := '';
        end;
        // アイドルタイム
        if RxDataAry[i * 2].Tick > 0  then
          Cells[1 + idx, k + 1] := RxDataAry[i * 2].Tick.ToString
        else if RxDataAry[i * 2 + 1].Tick > 0  then
          Cells[1 + idx, k + 1] := RxDataAry[i * 2 + 1].Tick.ToString
        else
          if Cells[1 + idx, k + 1] <> '' then Cells[1 + idx, k + 1] := '';

        Inc(idx);
        Inc(cnt);
        if idx >= 32 then begin
          idx := 0;
          Inc(nth);
        end;
        if cnt >= 64 then Break;
      end;
      // 余白を消す
      if cnt < 64 then begin
        for i := cnt to 64 - 1 do begin
          if i < 32 then begin
            k := 0;
            idx := i;
          end
          else begin
            k := 6;
            idx := i - 32;
          end;
          for j := 1 to 5 do
            if Cells[1 + idx, k + j] <> '' then Cells[1 + idx, k + j] := '';
        end;
      end;
      EndUpDate;
    end;
  end;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
  IniFile: TMemIniFile;
begin
  IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine(
    System.IOUtils.TPath.GetDocumentsPath, 'AKI232AND.ini'), TEncoding.UTF8);
  with IniFile do begin
    try
       WriteInteger('ComboBox', 'BaudRate', ComboBox1.ItemIndex);
       WriteInteger('ComboBox', 'J1<->J2', ComboBox2.ItemIndex);

       WriteBool('CheckBox', 'Bit8', CheckBox1.IsChecked);
       WriteBool('CheckBox', 'Bit8+1', CheckBox2.IsChecked);
    finally
      Free;
    end;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  IniFile: TMemIniFile;
begin
  Label1.Text := '';
  Label2.Text := '';

  // 横画面に固定
  Application.FormFactor.Orientations :=
    [TFormOrientation.Landscape, TFormOrientation.InvertedLandscape];

  IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine(
    System.IOUtils.TPath.GetDocumentsPath, 'AKI232AND.ini'), TEncoding.UTF8);
  with IniFile do begin
    try
      ComboBox1.ItemIndex := ReadInteger('ComboBox', 'BaudRate', 6);
      ComboBox2.ItemIndex := ReadInteger('ComboBox', 'J1<->J2', 0);
      CheckBox1.IsChecked := ReadBool('CheckBox', 'Bit8', True);
      CheckBox2.IsChecked := ReadBool('CheckBox', 'Bit8+1', False);
    finally
      Free;
    end;
  end;
end;

procedure TForm2.StringGrid1DrawColumnCell(Sender: TObject;
  const Canvas: TCanvas; const Column: TColumn; const Bounds: TRectF;
  const Row: Integer; const Value: TValue; const State: TGridDrawStates);
// セル描画
var
  s: string;
  c: Cardinal;
  ARect : TRectF;
begin
  if Column.Index = 0 then begin
  end
  else begin
    // データ番号
    if (Row = 0) or (Row = 6) then begin
      if Column.Index > 0 then begin
        ARect := Bounds;
        ARect.Left   := Bounds.Left - 2;
        ARect.Right  := Bounds.Right + 2;

        if (StringGrid1.Cells[Column.Index, Row + 2] = '') and
           (StringGrid1.Cells[Column.Index, Row + 4] = '') then
          Canvas.Fill.Color := TAlphaColorRec.Grey
        else
          Canvas.Fill.Color := TAlphaColorRec.Silver;//.Darkcyan;

        Canvas.FillRect(ARect, 0, 0, AllCorners, 1, TCornerType.Round );
        Canvas.DrawRect(Bounds, 0, 0, AllCorners, 1);
        if Column.Index + GStartIndex div 2 <= RxDataIndex div 2 then begin
          if Row = 0 then
            s := IntToStr(Column.Index + GStartIndex div 2)
          else
            s := IntToStr(32 + Column.Index + GStartIndex div 2);
          Canvas.Font.Size := 11;
          Canvas.Fill.Color := TAlphaColorRec.White;
          Canvas.FillText(Bounds, s, False, 1.0, [], TTextAlign.Center);
        end;
      end;
    end
    // アイドル時間(100ms単位)
    else if (Row = 1) or (Row = 7) then begin
      s := StringGrid1.Cells[Column.Index, Row];
      if s <> '' then begin
        Canvas.Fill.Color := claOrange;
        Canvas.FillRect(Bounds, 0, 0, AllCorners, 1, TCornerType.Round );
        Canvas.Fill.Color := claBlack;
        Canvas.Font.Size := 12;
        Canvas.FillText(Bounds, s, False, 1.0, [], TTextAlign.Leading);
      end;
    end
    // 16進表示を色分け
    else if Row mod 2  = 0 then begin
      if not Value.IsEmpty then s := Value.ToString
      else s := '';
      if s <> '' then begin
        c := StrToInt('$' + s);
        if (c < $20) or (c = $7E) then Canvas.Fill.Color := claRed
        else begin
          if (Row = 2) or (Row = 8) then Canvas.Fill.Color   := claLime
          else Canvas.Fill.Color   := claCyan;
        end;
        // 透過で塗りつぶし
        Canvas.FillRect( Bounds, 0, 0, AllCorners, 0.4, TCornerType.Round );
      end;
    end
    // 制御コードを表示
    else begin
      s := StringGrid1.Cells[Column.Index, Row - 1];
      if s <> '' then begin
        c := StrToInt('$' + s);
        if (c < $20) or (c = $7E) then begin
          Canvas.Fill.Color := claYellow;
          Canvas.FillRect(Bounds, 0, 0, AllCorners, 1, TCornerType.Round );
          Canvas.Fill.Color := claBlack;
          if c < $20 then s := ControlCode[c]
          else s := ControlCode[$20];
          Canvas.Font.Size := 11;
          Canvas.FillText(Bounds, s, False, 1.0, [], TTextAlign.Center);
        end;
      end;
    end
  end;
end;

// -----------------------------------------------------------------------------
procedure TComReadThread.ComRead;
var
  ret : integer;
  AData : TBytes;
  i : integer;
  Ticks : Cardinal;
  idl : integer;
  res : string;
begin
  GloopFlag := True;
  // ボーレート計測
  if GCmdMode = 101 then begin
    while not Terminated and GloopFlag do begin
      if Form2.Uni232C1.Connect then begin
        SetLength(AData, 64);
        ret := Form2.Uni232C1.Read(64, @AData[0]);
        if ret > 0 then begin
          for i := 0 to ret -1 do begin
            RxDataAry[RxDataIndex].Data := AData[i];
            Inc(RxDataIndex);
          end;
          {
          Synchronize(procedure begin
            Form2.Label2.Text := 'Read Data = ' + RxDataIndex.ToString;
          end);
          }
          if RxDataIndex > 100 then
            GLoopFlag := False;
        end
        else begin
          //Sleep(1);
          Synchronize(procedure begin
            if Form2.Label2.Text <> (RxDataIndex div 2).ToString then
              Form2.Label2.Text := (RxDataIndex div 2).ToString;
          end);
        end;
      end
      else
        GloopFlag := False;
    end;
  end
  // モニタリング
  else begin
    while not Terminated and GloopFlag do begin
      if Form2.Uni232C1.Connect then begin
        SetLength(AData, 64);
        ret := Form2.Uni232C1.Read(64, @AData[0]);
        if ret > 0 then begin

          Ticks := TThread.GetTickCount;
          if RxDataIndex > 0 then begin
            idl := (Ticks - GTicks) div 100;
            if idl > 255 then idl := 255;
            RxDataAry[RxDataIndex].Tick := idl;
          end;
          GTicks := Ticks;
          for i := 0 to ret - 1  do begin
            RxDataAry[RxDataIndex].Data := AData[i];
            Inc(RxDataIndex);
            if RxDataIndex >= 2048 then begin
              GloopFlag := False;
              break;
            end;
          end;
          {
          Synchronize(procedure begin
            Form2.Label2.Text := (RxDataIndex div 2).ToString;
          end);
          }
        end
        else begin
          //Sleep(1);
          Synchronize(procedure begin
            if Form2.Label2.Text <> (RxDataIndex div 2).ToString then
              Form2.Label2.Text := (RxDataIndex div 2).ToString;
          end);
        end;
      end
      else
        GloopFlag := False;
    end;
  end;
  Synchronize(procedure begin
    if Form2.Label2.Text <> (RxDataIndex div 2).ToString then
      Form2.Label2.Text := (RxDataIndex div 2).ToString;
  end);

  // モニタリング、ボーレート計測終了
  if Form2.Uni232C1.Connect then begin
    SetLength(AData, 64);
    AData := TEncoding.ANSI.GetBytes('E');
    Form2.Uni232C1.Write(1, @AData[0]);
    Sleep(100);
    Form2.Uni232C1.Close;
    if GCmdMode = 101 then begin
      if RxDataIndex > 100 then begin
        Synchronize(procedure begin
          ShowMessage(RateMesure);
        end);
      end;
    end;
  end;
  if GCmdMode = 101 then begin
    Synchronize(procedure begin
      Form2.Button5.Text := 'ボーレート計測';
    end);
  end
  else begin
    res := CheckRxData;
    if res <> ''then
      Synchronize(procedure begin
        Form2.Label2.Text := Form2.Label2.Text + ' (' + res + ')';
      end);
  end;
  Synchronize(procedure begin
    Form2.DispRxData;
    Form2.Button1.Enabled := True;
    Form2.Button5.Enabled := True;
  end);
  GCmdMode := 0;
  Beep(3);
end;

constructor TComReadThread.Create;
begin
  // スレッドを生成、直ちに実行
  inherited Create(False);
  // スレッド終了時、スレッドオブジェクトを破棄
  FreeOnTerminate := True;
end;

procedure TComReadThread.Execute;
begin
  ComRead;
end;

procedure TForm2.Button1Click(Sender: TObject);
// [START]
var
  AData : TBytes;
  ret : integer;
  i: Integer;
begin
  GCmdMode := 0;
  Label1.Text := '';
  SetLength(AData, 64);
  Uni232C1.BaudRate := 115200;
  Uni232C1.ByteSize := Bit8;
  Uni232C1.StopBits := StopBit1;
  Uni232C1.ParityBits := ParityNone;

  Uni232C1.Open ;
  Sleep(100);
  if Uni232C1.Connect then begin
    Button1.Enabled := False;
    Button5.Enabled := False;

    Beep(1);
    { [ BaudRate ];
      '0'   600bps
      '1'  1200bps
      '2'  2400bps
      '3'  4800bps
      '4'  9600bps
      '5' 14400bps
      '6' 19200bps
      '7' 28800bps
      '8' 38400bps
      '9' 57600bps }
    // ボーレート
    AData := TEncoding.ANSI.GetBytes(ComboBox1.ItemIndex.ToString);
    Uni232C1.Write(1, @AData[0]);
    Sleep(100);

    SetLength(AData, 64);
    Uni232C1.Read(64, @AData[0]);

    // 'V'	ファームウエアのバージョンデータ要求
    SetLength(AData, 64);
    AData := TEncoding.ANSI.GetBytes('V');
    Uni232C1.Write(1, @AData[0]);
    Sleep(100);
    SetLength(AData, 64);
    ret := Uni232C1.Read(64, @AData[0]);
    if ret > 0 then begin
      SetLength(AData, ret);
      Label1.Text := TEncoding.ANSI.GetString(AData);
    end;

    SetLength(AData, 64);
    // 8Bit / 8+1Bit (Parity)
    if CheckBox1.IsChecked then AData := TEncoding.ANSI.GetBytes('d')
    else AData := TEncoding.ANSI.GetBytes('D');
    Uni232C1.Write(1, @AData[0]);
    Sleep(100);

    SetLength(AData, 64);
    Uni232C1.Read(64, @AData[0]);

    // モニタリング開始
    case ComboBox2.ItemIndex of
      0 : AData := TEncoding.ANSI.GetBytes('G'); // 双方向
      1 : AData := TEncoding.ANSI.GetBytes('A'); // J2->J1
      2 : AData := TEncoding.ANSI.GetBytes('B'); // J1->J2
    end;
    Uni232C1.Write(1, @AData[0]);

    Sleep(100);
    SetLength(AData, 64);
    Uni232C1.Read(64, @AData[0]);

    RxDataIndex := 0;
    // データ表示の開始データ位置
    GStartIndex := 0;
    for i := 0 to 2047 do RxDataAry[i].Tick := 0;

    ThComRead := TComReadThread.Create;
  end;

end;

procedure TForm2.Button2Click(Sender: TObject);
// [STOP]
begin
  GLoopFlag := False;
end;

procedure TForm2.Button3Click(Sender: TObject);
// [<]ボタン
begin
  Button3.Enabled := False;
  if GStartIndex > 0 then begin
    GStartIndex := GStartIndex - 64;
    if GSTartIndex < 0 then GStartIndex := 0;
    DispRxData;
  end;
  Button3.Enabled := True;
end;

procedure TForm2.Button4Click(Sender: TObject);
// [>] ボタン
begin
  Button4.Enabled := False;
  if GStartIndex < RxDataIndex - 64 then begin
    GStartIndex := GStartIndex + 64;
    DispRxData;
  end;
  Button4.Enabled := True;
end;

procedure TForm2.Button5Click(Sender: TObject);
// ボーレート計測
var
  AData : TBytes;
  ret : integer;
  i : integer;
begin
  if Button5.Text <> '計測中止' then begin
    Label1.Text := '';
    if Button1.Enabled then begin
      Uni232C1.BaudRate := 115200;
      Uni232C1.ByteSize := Bit8;
      Uni232C1.StopBits := StopBit1;
      Uni232C1.ParityBits := ParityNone;
      Uni232C1.Open;
      Sleep(100);
      if Uni232C1.Connect then begin
        Beep(1);
        Button1.Enabled := False;
        Button5.Text := '計測中止';

        // 'V'	ファームウエアのバージョンデータ要求
        SetLength(AData, 64);
        AData := TEncoding.ANSI.GetBytes('V');
        Uni232C1.Write(1, @AData[0]);

        Sleep(100);
        SetLength(AData, 64);
        ret := Uni232C1.Read(64, @AData[0]);

        if ret > 0 then begin
          SetLength(AData, ret);
          Label1.Text := TEncoding.ANSI.GetString(AData);
        end;

        RxDataIndex := 0;
        GStartIndex := 0;

        SetLength(AData, 64);
        AData := TEncoding.ANSI.GetBytes('M');
        Uni232C1.Write(1, @AData[0]);
        Sleep(100);
        for i := 0 to 2047 do RxDataAry[i].Tick := 0;
        GCmdMode := 101;
        ThComRead := TComReadThread.Create;
      end;
    end;
  end
  else begin
    if Uni232C1.Connect then begin
      GloopFlag := False;
    end;
  end;
end;

procedure TForm2.CheckBox1Change(Sender: TObject);
begin
  if CheckBox1.IsChecked and CheckBox2.IsChecked then
    CheckBox2.IsChecked := False
  else if not CheckBox1.IsChecked and not CheckBox2.IsChecked then
    CheckBox2.IsChecked := True;

end;

procedure TForm2.CheckBox2Change(Sender: TObject);
begin
  if CheckBox1.IsChecked and CheckBox2.IsChecked then
    CheckBox1.IsChecked := False
  else if not CheckBox1.IsChecked and not CheckBox2.IsChecked then
    CheckBox1.IsChecked := True;
end;

end.