(*
说明:全志A20的LRADC底层操作封装类。分为两个部分:
1.TLRADCGROUP类,可对寄存器直接操作,单例;
2.TLRADC类,对具体的LRADC通道实现了一些功能的简化。
作者:tjCFeng
邮箱:tjCFeng@163.com
更新日期:2014.12.06
*)

unit LRADC;

{$mode objfpc}{$H+}

interface

uses SysUtils, A20;

type
  TChannel = (LRADC_0, LRADC_1);
  TLRADCINT = (ADCDATA, KEYDOWN, HOLD, ALRDYHOLD, KEYUP);
  TLRADCINTs = set of TLRADCINT;

  TLRADCGROUP = class
  private
    class var FInstance: TLRADCGROUP;
    class function GetInstance: TLRADCGROUP; static;
  public
    class procedure Release;
    class property Instance: TLRADCGROUP read GetInstance;

  private
    FLRADC_BASE: ^LongWord;
    FLRADC_CTRL: TGROUP1_REG;
    FLRADC_INTC: TGROUP1_REG;
    FLRADC_INTS: TGROUP1_REG;
    FLRADC_DATA: TGROUP2_REG;

    constructor Create;
    destructor Destroy; override;
  public
    procedure Start;
    procedure Stop;
    procedure ClearAllPending;
  public
    property LRADC_CTRL: TGROUP1_REG read FLRADC_CTRL;
    property LRADC_INTC: TGROUP1_REG read FLRADC_INTC;
    property LRADC_INTS: TGROUP1_REG read FLRADC_INTS;
    property LRADC_DATA: TGROUP2_REG read FLRADC_DATA;
  end;

  TLRADC = class
  private
    FChannel: TChannel;
    FINTs: TLRADCINTs;

    FLRADC_DATA: ^LongWord;
  private
    procedure SetINTs(INTs: TLRADCINTs);
    function GetDATA: Byte;
  public
    constructor Create(Channel: TChannel);

    function GetStatus(INT: TLRADCINT): Boolean;
    procedure ClearPendings(INTs: TLRADCINTs);
  public
    property Channel: TChannel read FChannel;
    property INTs: TLRADCINTs read FINTs write SetINTs default [];
    property Data: Byte read GetDATA;
  end;

implementation

const
  LRADC_BASE = $01C22800;

(*LRADC Group******************************************************************)
class function TLRADCGROUP.GetInstance: TLRADCGROUP;
begin
  if FInstance = nil then FInstance:= TLRADCGROUP.Create;
  Result:= FInstance;
end;

class procedure TLRADCGROUP.Release;
begin
  FreeAndNil(FInstance);
end;

constructor TLRADCGROUP.Create;
var Base: LongWord;
begin
  inherited Create;

  FLRADC_BASE:= TA20.Instance.GetMMap(LRADC_BASE);
  Base:= LongWord(FLRADC_BASE) + TA20.Instance.BaseOffset(LRADC_BASE);

  FLRADC_CTRL:= Pointer(Base + $00);
  FLRADC_INTC:= Pointer(Base + $04);
  FLRADC_INTS:= Pointer(Base + $08);
  FLRADC_DATA[0]:= Pointer(Base + $0C);
  FLRADC_DATA[1]:= Pointer(Base + $10);
end;

destructor TLRADCGROUP.Destroy;
begin
  TA20.Instance.FreeMMap(FLRADC_BASE);

  inherited Destroy;
end;

procedure TLRADCGROUP.Start;
begin
  FLRADC_CTRL^:= FLRADC_CTRL^ or ($1 shl 0);
end;

procedure TLRADCGROUP.Stop;
begin
  FLRADC_CTRL^:= FLRADC_CTRL^ and  not ($1 shl 0);
end;

procedure TLRADCGROUP.ClearAllPending;
begin
  FLRADC_INTS^:= $00001F1F;
end;

(*LRADC Channel****************************************************************)
constructor TLRADC.Create(Channel: TChannel);
begin
  inherited Create;

  FChannel:= Channel;
  FLRADC_DATA:= TLRADCGROUP.Instance.LRADC_DATA[Ord(FChannel)];
end;

function TLRADC.GetDATA: Byte;
begin
  Result:= Byte(FLRADC_DATA^);
end;

function TLRADC.GetStatus(INT: TLRADCINT): Boolean;
var INTBIT: LongWord;
begin
  INTBIT:= Ord(FChannel) * 8 + Ord(INT);
  Result:= (TLRADCGROUP.Instance.LRADC_INTS^ and INTBIT) = INTBIT;
end;

procedure TLRADC.SetINTs(INTs: TLRADCINTs);
  procedure SetINT(IndexINT: Byte; ED: Boolean);
  begin
    with TLRADCGROUP.Instance do
    if ED then
      LRADC_INTC^:= LRADC_INTC^ or ($1 shl (Ord(FChannel) * 8) + IndexINT)
    else
      LRADC_INTC^:= LRADC_INTC^ and  not ($1 shl (Ord(FChannel) * 8) + IndexINT);
  end;
begin
  FINTs:= INTs;
  SetINT(Ord(ADCDATA), ADCDATA in INTs);
  SetINT(Ord(KEYDOWN), KEYDOWN in INTs);
  SetINT(Ord(HOLD), HOLD in INTs);
  SetINT(Ord(ALRDYHOLD), ALRDYHOLD in INTs);
  SetINT(Ord(KEYUP), KEYUP in INTs);
end;

procedure TLRADC.ClearPendings(INTs: TLRADCINTs);
  procedure ClearPending(IndexINT: Byte);
  begin
    with TLRADCGROUP.Instance do
    LRADC_INTS^:= LRADC_INTS^ or ($1 shl Ord(FChannel) * 8 + IndexINT);
  end;
begin
  if (ADCDATA in INTs) then ClearPending(Ord(DATA));
  if (KEYDOWN in INTs) then ClearPending(Ord(KEYDOWN));
  if (HOLD in INTs) then ClearPending(Ord(HOLD));
  if (ALRDYHOLD in INTs) then ClearPending(Ord(ALRDYHOLD));
  if (KEYUP in INTs) then ClearPending(Ord(KEYUP));
end;


finalization
  TLRADCGROUP.Instance.Release;

end.