// =====================================================
//
//   miniLA_win - I2C Decoder
//
//   (c) miniLA Team
//
// =====================================================
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

unit dlgDecI2C;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons,
  uDecUtils;

type
  TfrmDecI2C = class(TForm)
    cbChannelSDA: TComboBox;
    Label1: TLabel;
    cbInvertSDA: TCheckBox;
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    GroupBox1: TGroupBox;
    Label6: TLabel;
    cbInvertSCL: TCheckBox;
    cbChannelSCL: TComboBox;
    gbDecodeRange: TGroupBox;
    rbAllData: TRadioButton;
    rbCursors: TRadioButton;
    procedure btnOKClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    ChannelSel:  TChannelSel;

    channel_sda: integer;
    channel_scl: integer;
    sda, sda_last: byte;
    scl, scl_last: byte;

    function SDA_i(val:byte):cardinal;
    function SCL_i(val:byte):cardinal;
    function SDA_o(val:cardinal):byte;
    function SCL_o(val:cardinal):byte;
    function loc_data_find_change:boolean;
  public
    { Public declarations }
  end;

var
  frmDecI2C: TfrmDecI2C;

implementation

uses
   uUtils,
   uLATypes,
   uDecData,
   uINIFile,
   dlgDecDisplay,
   dlgMain;

{$R *.dfm}

procedure TfrmDecI2C.FormCreate(Sender: TObject);
begin
   cbChannelSDA.ItemIndex := INIFile.ReadInteger('DEC_I2C','CHANNEL_SDA', 0);
   cbInvertSDA.Checked    := INIFile.ReadBool('DEC_I2C','INVERT_SDA', false);
   cbChannelSCL.ItemIndex := INIFile.ReadInteger('DEC_I2C','CHANNEL_SCL', 1);
   cbInvertSCL.Checked    := INIFile.ReadBool('DEC_I2C','INVERT_SCL', false);
   rbAllData.Checked      := INIFile.ReadBool('DEC_I2C','DEC_FULL_RANGE', true);
   rbCursors.Checked	  := not(rbAllData.Checked);
end;


procedure TfrmDecI2C.FormDestroy(Sender: TObject);
begin
   INIFile.WriteInteger('DEC_I2C','CHANNEL_SDA', cbChannelSDA.ItemIndex);
   INIFile.WriteBool('DEC_I2C','INVERT_SDA', cbInvertSDA.Checked);
   INIFile.WriteInteger('DEC_I2C','CHANNEL_SCL', cbChannelSCL.ItemIndex);
   INIFile.WriteBool('DEC_I2C','INVERT_SCL', cbInvertSCL.Checked);
   INIFile.WriteBool('DEC_I2C','DEC_FULL_RANGE', rbAllData.Checked);
end;


procedure TfrmDecI2C.FormActivate(Sender: TObject);
begin
   ChannelSel := TChannelSel.Create;
   ChannelSel.Add(cbChannelSDA);
   ChannelSel.Add(cbChannelSCL);
end;

function TfrmDecI2C.SDA_i(val:byte):cardinal;
begin
   result := val shl channel_sda;
end;

function TfrmDecI2C.SCL_i(val:byte):cardinal;
begin
   result := val shl channel_scl;
end;

function TfrmDecI2C.SDA_o(val:cardinal):byte;
begin
   result := (val shr channel_sda) and $1;
end;

function TfrmDecI2C.SCL_o(val:cardinal):byte;
begin
   result := (val shr channel_scl) and $1;
end;


function TfrmDecI2C.loc_data_find_change:boolean;
var
   read_err	: boolean;
begin
   sda_last := sda;
   scl_last := scl;
   read_err := data_find_change(SDA_i(1) or SCL_i(1));
   sda := SDA_o(sample);
   scl := SCL_o(sample);
   result := read_err;
end;


procedure TfrmDecI2C.btnOKClick(Sender: TObject);
var
   read_err	: boolean;
   state	: (ST_IDLE, ST_DATA, ST_ACK);
   bit_cnt	: byte;
   address_byte	: boolean;
   rec_byte	: word;

   err_msg	: string;
   rep_str	: string;
begin
   err_msg := '';
   frmDecDisplay.lbResults.Clear;

   channel_sda := ChannelSel.GetChannelNumber(cbChannelSDA);
   if (channel_sda < 0) then
      err_msg := 'SDA channel';

   channel_scl := ChannelSel.GetChannelNumber(cbChannelSCL);
   if (channel_scl < 0) then
      err_msg := 'SCL channel';

   if ChannelSel.MultiSelect then
      err_msg := 'channel selection';

   if err_msg <> '' then
    begin
      MessageDlg('Invalid '+err_msg, mtError,[mbOK],0);
      exit;
    end;

   ChannelSel.Free;
   Hide;
   frmMain.StatusBar1.Panels[5].Text := 'Decoding I2C protocol ...';
   frmMain.StatusBar1.Repaint;

   sample_inv := 0;

   if cbInvertSDA.Checked then
      sample_inv := sample_inv or SDA_i(1);

   if cbInvertSCL.Checked then
      sample_inv := sample_inv or SCL_i(1);

   if rbAllData.Checked then
      data_init(0, frmMain.LogPanel.Display.DataHold.MaxPosition)	// initialize sample read routine
   else
      data_init(frmMain.LogPanel.Cursors[0].Position,
		frmMain.LogPanel.Cursors[1].Position);

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) start of decoding', [sample_n, TmToStr(time_p,2)]));

   bit_cnt := 0;
   address_byte := false;
   rec_byte := 0;
   read_err := false;
   state := ST_IDLE;

   // get initial value
   sda := SDA_o(sample);
   scl := SCL_o(sample);

   while (not read_err) do
    begin
      read_err := read_err or loc_data_find_change();
      if (read_err) then
	 break;

      // start bit
      // SCL=1 and falling edge of SDA
      if (((sda_last<>0) and (sda=0)) and (scl_last<>0) and (scl<>0)) then
       begin
	 if (bit_cnt <> 0) then
	    frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, start bit after incomplete transfer', [sample_n, TmToStr(time_p,2)]))
	 else if (state = ST_DATA) then
            frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Repeated start bit', [sample_n, TmToStr(time_p,2)]));

	 bit_cnt := 0;
	 rec_byte := 0;
	 address_byte := true;
	 state := ST_DATA;
       end;

      // data(ack) bit
      // rising edge of SCL
      if ((scl_last=0) and (scl<>0)) then
       begin
	 // data bit
	 if (state = ST_DATA) then
	  begin
	    rec_byte := (rec_byte shl 1) or sda;
	    inc(bit_cnt);
	    if (bit_cnt = 8) then
	       state := ST_ACK;
          end

	 // ack bit
	 else if (state = ST_ACK) then
	  begin
	    if (sda<>0) then
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, no acknowledge', [sample_n, TmToStr(time_p,2)]));

	    if (address_byte) then
	     begin
	       address_byte := false;
               if (rec_byte and $01) <> 0 then
                  rep_str := 'Read from'
               else
                  rep_str := 'Write to';
               frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) %s 0x%.2X', [sample_n, TmToStr(time_p,2),rep_str,(rec_byte and $FE)]))
             end
	    else
	     begin
	       if (rec_byte >= 31) then
                  rep_str :=chr(rec_byte)
               else
                  rep_str := '';
               frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) 0x%.2X %s', [sample_n, TmToStr(time_p,2),rec_byte,rep_str]))
	     end;
	    rec_byte := 0;
	    bit_cnt := 0;
	    state := ST_DATA;
	  end;
       end;

      // stop bit
      // SCL=1 and rising edge of SDA
      if (((sda_last=0) and (sda<>0)) and (scl_last<>0) and (scl<>0)) then
       begin
	 if     (state <> ST_IDLE)			// suppress error when sampling starts at middle of transmission
            and ((bit_cnt <> 1) or (address_byte)) then	// note: (bit_cnt<>1) = rising edge after ACK clock pulse
            frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, stop bit after incomplete transfer', [sample_n, TmToStr(time_p,2)]));
	 bit_cnt := 0;
	 state := ST_IDLE;
       end;
    end;

   if (state <> ST_IDLE) then
      frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) last byte not fully decoded.', [sample_n, TmToStr(time_p,2)]));

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) end of decoding', [sample_n, TmToStr(time_p,2)]));   ModalResult := mrOK;

   ModalResult := mrOK;
   frmDecDisplay.Caption := 'Decoded data - I2C';
   frmDecDisplay.Show;
   frmMain.StatusBar1.Panels[5].Text := '';
   frmMain.StatusBar1.Repaint;
end;


procedure TfrmDecI2C.btnCancelClick(Sender: TObject);
begin
   ChannelSel.Free;
end;


end.

