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

unit Timer;

{$mode objfpc}{$H+}

interface

uses SysUtils, A20;

type
  TChannel = (Timer_0, Timer_1, Timer_2, Timer_3, Timer_4, Timer_5);
  TMode = (Loop, Single);
  TCLK = (OSC32K, OSC24M, PLL6_6);
  TPrescal = (Div1, Div2, Div4, Div8, Div16, Div32, Div64, Div128);

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

  private
    FTMR_BASE: ^LongWord;

    constructor Create;
    destructor Destroy; override;
  protected
    FTMR_IRQ_CTRL: TGROUP1_REG;
    FTMR_IRQ_STA: TGROUP1_REG;
    FTMR_CTRL: TGROUP6_REG;
    FTMR_INTV_VALUE: TGROUP6_REG;
    FTMR_CURR_VALUE: TGROUP6_REG;
  public
    property TMR_IRQ_CTRL: TGROUP1_REG read FTMR_IRQ_CTRL;
    property TMR_IRQ_STA: TGROUP1_REG read FTMR_IRQ_STA;
    property TMR_CTRL: TGROUP6_REG read FTMR_CTRL;
    property TMR_INTV_VALUE: TGROUP6_REG read FTMR_INTV_VALUE;
    property TMR_CURR_VALUE: TGROUP6_REG read FTMR_CURR_VALUE;
  end;
  
  TTimer = class
  private
    FChannel: TChannel;
    FCHBit: LongWord;
    FMode: TMode;
    FAutoReload: Boolean;
    FCLK: TCLK;
    FPrescal: TPrescal;
    FCNT: LongWord;
    FCUR: LongWord;

    FTIM_IRQ_CTL: ^LongWord;
    FTIM_IRQ_STA: ^LongWord;
    FTIM_CTRL: ^LongWord;
    FTIM_INTV: ^LongWord;
    FTIM_CURR: ^LongWord;
  protected
    procedure SetMode(Value: TMode);
    procedure SetAutoReload(Value: Boolean);
    procedure SetCLK(Value: TCLK);
    procedure SetPrescal(Value: TPrescal);
    procedure SetCNT(Value: LongWord);
    procedure SetCUR(Value: LongWord);
    procedure SetINT(INT: Boolean);
    function GetINT: Boolean;
  public
    constructor Create(Channel: TChannel);
    destructor Destroy; override;
    procedure ClearPending;
    procedure Start;
    procedure Stop;
  public
    property Channel: TChannel read FChannel;
    property Mode: TMode read FMode write SetMode default Single;
    property AutoReload: Boolean read FAutoReload write SetAutoReload default True;
    property CLK: TCLK read FCLK write SetCLK default OSC24M;
    property Prescal: TPrescal read FPrescal write SetPrescal default Div1;
    property CNT: LongWord read FCNT write SetCNT default 0;
    property CUR: LongWord read FCUR write SetCUR default 0;
    property INT: Boolean read GetINT write SetINT default False;
  end;
  
implementation

const
  TMR_BASE = $01C20C00;

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

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

constructor TTimerGroup.Create;
var Base: LongWord; I: Byte;
begin
  inherited Create;

  FTMR_BASE:= TA20.Instance.GetMMap(TMR_BASE);
  Base:= LongWord(FTMR_BASE) + TA20.Instance.BaseOffset(TMR_BASE);

  FTMR_IRQ_CTRL:= Pointer(Base + $00);
  FTMR_IRQ_STA:= Pointer(Base + $04);
  
  for I:= 0 to 5 do
  begin
    FTMR_CTRL[I]:= Pointer(Base + (I + 1) * $10);
    FTMR_INTV_VALUE[I]:= Pointer(Base + (I + 1) * $10 + $04);
    FTMR_CURR_VALUE[I]:= Pointer(Base + (I + 1) * $10 + $08);
  end;
  FTMR_CURR_VALUE[3]:= nil;
end;

destructor TTimerGroup.Destroy;
begin
  TA20.Instance.FreeMMap(FTMR_BASE);

  inherited Destroy;
end;

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

  FChannel:= Channel;
  FCHBit:= ($1 shl Ord(FChannel));

  with TTimerGROUP.Instance do
  begin
    FTIM_IRQ_CTL:= TMR_IRQ_CTRL;
    FTIM_IRQ_STA:= TMR_IRQ_STA;
    FTIM_CTRL:= TMR_CTRL[Ord(FChannel)];
    FTIM_INTV:= TMR_INTV_VALUE[Ord(FChannel)];
    FTIM_CURR:= TMR_CURR_VALUE[Ord(FChannel)];
  end;

  FMode:= Single;
  SetMode(Single);
  FAutoReload:= True;
  SetAutoReload(True);
  FCLK:= OSC24M;
  SetCLK(OSC24M);
  FPrescal:= Div1;
  SetPrescal(Div1);
  FCNT:= 0;
  FCUR:= 0;
  SetINT(False);
end;

destructor TTimer.Destroy;
begin
  Stop;
end;

procedure TTimer.SetMode(Value: TMode);
begin
  FMode:= Value;
  case FMode of
  Loop: FTIM_CTRL^:= FTIM_CTRL^ and not ($1 shl 7);
  Single: FTIM_CTRL^:= FTIM_CTRL^ or ($1 shl 7);
  end;
end;

procedure TTimer.SetAutoReload(Value: Boolean);
begin
  FAutoReload:= Value;
  
  case FAutoReload of
  True: FTIM_CTRL^:= FTIM_CTRL^ and not ($1 shl 1);
  False: FTIM_CTRL^:= FTIM_CTRL^ or ($1 shl 1);
  end;  
end;

procedure TTimer.SetCLK(Value: TCLK);
begin
  FCLK:= Value;
  
  FTIM_CTRL^:= FTIM_CTRL^ and not ($3 shl 2);
  FTIM_CTRL^:= FTIM_CTRL^ or (Ord(FCLK) shl 2);
end;

procedure TTimer.SetPrescal(Value: TPrescal);
begin
  FPrescal:= Value;
  
  FTIM_CTRL^:= FTIM_CTRL^ and not ($3 shl 4);
  FTIM_CTRL^:= FTIM_CTRL^ or (Ord(FPrescal) shl 4)
end;

procedure TTimer.SetCNT(Value: LongWord);
begin
  FCNT:= Value;
  FTIM_INTV^:= FCNT;
end;

procedure TTimer.SetCUR(Value: LongWord);
begin
  FCUR:= Value;
  FTIM_CURR^:= FCUR;
end;

procedure TTimer.SetINT(INT: Boolean);
begin
  case INT of
  False: FTIM_IRQ_CTL^:= FTIM_IRQ_CTL^ and not FCHBit;
  True: FTIM_IRQ_CTL^:= FTIM_IRQ_CTL^ or FCHBit;
  end;
end;

function TTimer.GetINT: Boolean;
begin
  Result:= (FTIM_IRQ_STA^ and FCHBit) = FCHBit;
end;

procedure TTimer.ClearPending;
begin
  FTIM_IRQ_STA^:= FTIM_IRQ_STA^ or FCHBit;
end;

procedure TTimer.Start;
begin
  FTIM_CTRL^:= FTIM_CTRL^ or $1;
end;

procedure TTimer.Stop;
begin
  FTIM_CTRL^:= FTIM_CTRL^ and not $1;
end;


finalization
  TTimerGROUP.Instance.Release;
  
end.