// =====================================================
//
//   miniLA_win - SPI 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 dlgDecSPI;

interface

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

type
  TfrmDecSPI = class(TForm)
    cbChannelMOSI: TComboBox;
    Label1: TLabel;
    cbInvertMOSI: TCheckBox;
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    GroupBox1: TGroupBox;
    Label6: TLabel;
    cbInvertMISO: TCheckBox;
    cbChannelMISO: TComboBox;
    Label2: TLabel;
    Label3: TLabel;
    cbChannelSCK: TComboBox;
    cbChannelSS: TComboBox;
    cbInvertSS: TCheckBox;
    cbInvertSCK: TCheckBox;
    Label4: TLabel;
    cbSPIMode: TComboBox;
    Label5: TLabel;
    cbBitOrder: 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_mosi : integer;
    channel_miso : integer;
    channel_sck : integer;
    channel_ss : integer;
    mosi : byte;
    miso : byte;
    sck, sck_last : byte;
    ss, ss_last : byte;

    function MOSI_i(val:byte):cardinal;
    function MISO_i(val:byte):cardinal;
    function SCK_i(val:byte):cardinal;
    function SS_i(val:byte):cardinal;

    function MOSI_o(val:cardinal):byte;
    function MISO_o(val:cardinal):byte;
    function SCK_o(val:cardinal):byte;
    function SS_o(val:cardinal):byte;

    function loc_data_find_change:boolean;
  public
    { Public declarations }
  end;

var
  frmDecSPI: TfrmDecSPI;

implementation

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

{$R *.dfm}

procedure TfrmDecSPI.FormCreate(Sender: TObject);
begin
   cbChannelMOSI.ItemIndex := INIFile.ReadInteger('DEC_SPI','CHANNEL_MOSI', 0);
   cbInvertMOSI.Checked    := INIFile.ReadBool('DEC_SPI','INVERT_MOSI', false);
   cbChannelMISO.ItemIndex := INIFile.ReadInteger('DEC_SPI','CHANNEL_MISO', 1);
   cbInvertMISO.Checked    := INIFile.ReadBool('DEC_SPI','INVERT_MISO', false);
   cbChannelSCK.ItemIndex  := INIFile.ReadInteger('DEC_SPI','CHANNEL_SCK', 2);
   cbInvertSCK.Checked     := INIFile.ReadBool('DEC_SPI','INVERT_SCK', false);
   cbChannelSS.ItemIndex   := INIFile.ReadInteger('DEC_SPI','CHANNEL_SS', 3);
   cbInvertSS.Checked      := INIFile.ReadBool('DEC_SPI','INVERT_SS', false);
   cbSPIMode.ItemIndex     := INIFile.ReadInteger('DEC_SPI','SPI_MODE', 0);
   cbBitOrder.ItemIndex    := INIFile.ReadInteger('DEC_SPI','BIT_ORDER', 1);
   rbAllData.Checked       := INIFile.ReadBool('DEC_SPI','DEC_FULL_RANGE', true);
   rbCursors.Checked	   := not(rbAllData.Checked);
end;

procedure TfrmDecSPI.FormDestroy(Sender: TObject);
begin
   INIFile.WriteInteger('DEC_SPI','CHANNEL_MOSI', cbChannelMOSI.ItemIndex);
   INIFile.WriteBool('DEC_SPI','INVERT_MOSI', cbInvertMOSI.Checked);
   INIFile.WriteInteger('DEC_SPI','CHANNEL_MISO', cbChannelMISO.ItemIndex);
   INIFile.WriteBool('DEC_SPI','INVERT_MISO', cbInvertMISO.Checked);
   INIFile.WriteInteger('DEC_SPI','CHANNEL_SCK', cbChannelSCK.ItemIndex);
   INIFile.WriteBool('DEC_SPI','INVERT_SCK', cbInvertSCK.Checked);
   INIFile.WriteInteger('DEC_SPI','CHANNEL_SS', cbChannelSS.ItemIndex);
   INIFile.WriteBool('DEC_SPI','INVERT_SS', cbInvertSS.Checked);
   INIFile.WriteInteger('DEC_SPI','SPI_MODE', cbSPIMode.ItemIndex);
   INIFile.WriteInteger('DEC_SPI','BIT_ORDER', cbBitOrder.ItemIndex);
   INIFile.WriteBool('DEC_SPI','DEC_FULL_RANGE', rbAllData.Checked);
end;


procedure TfrmDecSPI.FormActivate(Sender: TObject);
begin
   ChannelSel := TChannelSel.Create;
   ChannelSel.Add(cbChannelMOSI);
   ChannelSel.Add(cbChannelMISO);
   ChannelSel.Add(cbChannelSCK);
   ChannelSel.Add(cbChannelSS);
end;

function TfrmDecSPI.MOSI_i(val:byte):cardinal;
begin
   result := val shl channel_mosi;
end;

function TfrmDecSPI.MISO_i(val:byte):cardinal;
begin
   result := val shl channel_miso;
end;

function TfrmDecSPI.SCK_i(val:byte):cardinal;
begin
   result := val shl channel_sck;
end;

function TfrmDecSPI.SS_i(val:byte):cardinal;
begin
   result := val shl channel_ss;
end;

function TfrmDecSPI.MOSI_o(val:cardinal):byte;
begin
   result := (val shr channel_mosi) and $1;
end;

function TfrmDecSPI.MISO_o(val:cardinal):byte;
begin
   result := (val shr channel_miso) and $1;
end;

function TfrmDecSPI.SCK_o(val:cardinal):byte;
begin
   result := (val shr channel_sck) and $1;
end;

function TfrmDecSPI.SS_o(val:cardinal):byte;
begin
   result := (val shr channel_ss) and $1;
end;

function TfrmDecSPI.loc_data_find_change:boolean;
var
   read_err	: boolean;
begin
   sck_last := sck;
   ss_last := ss;
   read_err := data_find_change(SS_i(1) or SCK_i(1));
   mosi := MOSI_o(sample);
   miso := MISO_o(sample);
   sck := SCK_o(sample);
   ss := SS_o(sample);
   result := read_err;
end;


procedure TfrmDecSPI.btnOKClick(Sender: TObject);
var
   read_err	: boolean;

   state	: (ST_IDLE, ST_DATA);
   bit_cnt	: byte;
   mosi_byte	: byte;
   miso_byte	: byte;

   spi_mode	: integer;
   lsb_first	: boolean;

   err_msg	: string;
   rec_str1	: string;
   rec_str2	: string;
begin
   err_msg := '';
   frmDecDisplay.lbResults.Clear;

   channel_mosi := ChannelSel.GetChannelNumber(cbChannelMOSI);
   if (channel_mosi < 0) then
      err_msg := 'MOSI channel';

   channel_miso := ChannelSel.GetChannelNumber(cbChannelMISO);
   if (channel_miso < 0) then
      err_msg := 'MISO channel';

   channel_sck := ChannelSel.GetChannelNumber(cbChannelSCK);
   if (channel_sck < 0) then
      err_msg := 'SCK channel';

   channel_ss := ChannelSel.GetChannelNumber(cbChannelSS);
   if (channel_ss < 0) then
      err_msg := 'SS channel';

   spi_mode := cbSPIMode.ItemIndex;
   if (spi_mode < 0) then
      err_msg := 'SPI mode';

   case cbBitOrder.ItemIndex of
      0: lsb_first := true;
      1: lsb_first := false;
   else
    begin
      err_msg := 'bit order';
      lsb_first := true;
    end;
   end;

   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 SPI protocol ...';
   frmMain.StatusBar1.Repaint;

   sample_inv := 0;

   if cbInvertMOSI.Checked then
      sample_inv := sample_inv or MOSI_i(1);

   if cbInvertMISO.Checked then
      sample_inv := sample_inv or MISO_i(1);

   if cbInvertSCK.Checked then
      sample_inv := sample_inv or SCK_i(1);

   if cbInvertSS.Checked then
      sample_inv := sample_inv or SS_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;
   mosi_byte := 0;
   miso_byte := 0;
   state := ST_IDLE;
   read_err := false;

   // get initial value
   mosi := MOSI_o(sample);
   miso := MISO_o(sample);
   sck := SCK_o(sample);
   ss := SS_o(sample);

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

      // start of transmission (falling edge of SS)
      if ((ss_last<>0) and (ss=0)) then
       begin
	 mosi_byte := 0;
	 miso_byte := 0;
	 bit_cnt := 0;
	 if (   (((spi_mode = 0) or (spi_mode = 1)) and ((sck_last<>0) or (sck<>0)))
             or (((spi_mode = 2) or (spi_mode = 3)) and ((sck_last=0 ) or (sck=0 ))) ) then
	    frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, incorrect SCK level', [sample_n, TmToStr(time_p,2)]));
	 //frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Frame start', [sample_n, TmToStr(time_p,2)]));
	 state := ST_DATA;
       end;

      // rising/falling edge of SCK
      if (    (state = ST_DATA)
	  and (   (((sck_last=0 ) and (sck<>0)) and ((spi_mode = 0) or (spi_mode = 3)))		// rising edge
	       or (((sck_last<>0) and (sck=0 )) and ((spi_mode = 1) or (spi_mode = 2))))) then	// falling edge
       begin
	 if lsb_first then
	  begin
            mosi_byte := mosi_byte or (mosi shl bit_cnt);
            miso_byte := miso_byte or (miso shl bit_cnt);
          end
	 else
	  begin
            mosi_byte := (mosi_byte shl 1) or mosi;
            miso_byte := (miso_byte shl 1) or miso;
	  end;

	 inc(bit_cnt);
	 if (bit_cnt = 8) then
	  begin
	    bit_cnt := 0;
            if (miso_byte >= 31) then
               rec_str1 :=chr(miso_byte)
            else
	       rec_str1 := ' ';
            if (mosi_byte >= 31) then
	       rec_str2 :=chr(mosi_byte)
	    else
	       rec_str2 := ' ';
	    frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) MISO 0x%.2X %s MOSI 0x%.2X %s', [sample_n, TmToStr(time_p,2),miso_byte,rec_str1,mosi_byte,rec_str2]))
	  end;
       end;

      // end of transmission (rising edge of SS)
      if (((ss_last=0) and (ss<>0)) and (state = ST_DATA)) then
       begin
	 if (bit_cnt <> 0) then
	    frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, transmission not fully completed', [sample_n, TmToStr(time_p,2)]));

	 if (   (((spi_mode = 0) or (spi_mode = 1)) and ((sck_last <> 0) or (sck <> 0)))
	     or (((spi_mode = 2) or (spi_mode = 3)) and ((sck_last =  0) or (sck =  0))) ) then
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Error, incorrect SCK level', [sample_n, TmToStr(time_p,2)]));

	 //frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) Frame end', [sample_n, TmToStr(time_p,2)]));
	 frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) ---', [sample_n, TmToStr(time_p,2)]));
	 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 - SPI';
   frmDecDisplay.Show;
   frmMain.StatusBar1.Panels[5].Text := '';
   frmMain.StatusBar1.Repaint;
end;


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


end.

