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

unit PWM;

{$mode objfpc}{$H+}

interface

uses SysUtils, A20, GPIO;

type
  TChannel = (PWM_0, PWM_1);
  TPrescal =
    (P120, P180, P240, P360, P480, P720, P840, P960,
     P12K, P24K, P36K, P48K, P72K, P84K, P96K, P108K);

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

  private
    FPWM_BASE: ^LongWord;
    FPWM_CTRL: TGROUP1_REG;
    FPWM_PERIOD: TGROUP2_REG;

    constructor Create;
    destructor Destroy; override;
  public
    property PWM_CTRL: TGROUP1_REG read FPWM_CTRL;
    property PWM_PERIOD: TGROUP2_REG read FPWM_PERIOD;
  end;

  TPWM = class(TGPIO)
  private
    FChannel: TChannel;
    FCycle: Word;
    FDuty: Word;
    FPWM_PERIOD: ^LongWord;
  private
    procedure SetPrescale(Value: TPrescal);
    procedure SetCycle(Value: Word);
    procedure SetDuty(Value: Word);
  public
    constructor Create(Channel: TChannel);
    procedure Start;
    procedure Stop;
  public
    property Channel: TChannel read FChannel;
    property Prescale: TPrescal write SetPrescale;
    property Cycle: Word write SetCycle;
    property Duty: Word write SetDuty;
  end;

implementation

const
  PWM_BASE = $01C20C00;

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

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

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

  FPWM_BASE:= TA20.Instance.GetMMap(PWM_BASE);
  Base:= LongWord(FPWM_BASE) + TA20.Instance.BaseOffset(PWM_BASE);

  FPWM_CTRL:= Pointer(Base + $200);
  FPWM_PERIOD[0]:= Pointer(Base + $204);
  FPWM_PERIOD[1]:= Pointer(Base + $208);

  //FPWM_CTRL^:= FPWM_CTRL^ or (($1 shl 24) + ($1 shl 9)); //OSC24MHz
end;

destructor TPWMGROUP.Destroy;
begin
  FPWM_CTRL^:= 0;
  FPWM_PERIOD[0]^:= 0;
  FPWM_PERIOD[1]^:= 0;
  TA20.Instance.FreeMMap(FPWM_BASE);

  inherited Destroy;
end;

(*PWM Channel******************************************************************)
constructor TPWM.Create(Channel: TChannel);
begin
  FChannel:= Channel;

  case FChannel of
  PWM_0: inherited Create(PB, 2);
  PWM_1: inherited Create(PI, 3);
  end;
  SetFun(Fun2);

  FPWM_PERIOD:= TPWMGROUP.Instance.PWM_PERIOD[Ord(FChannel)];
  FPWM_PERIOD^:= 0;

  with TPWMGROUP.Instance do
  PWM_CTRL^:= PWM_CTRL^ or ($1 shl (Ord(FChannel) * 15 + 6)); //Special Clock
end;

procedure TPWM.SetPrescale(Value: TPrescal);
begin
  with TPWMGROUP.Instance do
  begin
    PWM_CTRL^:= PWM_CTRL^ and not ($F shl (Ord(FChannel) * 15));
    PWM_CTRL^:= PWM_CTRL^ or (Ord(Value) shl (Ord(FChannel) * 15));
  end;
end;

procedure TPWM.SetCycle(Value: Word);
begin
  FCycle:= Value;
  FPWM_PERIOD^:= FPWM_PERIOD^ and $0000FFFF;
  FPWM_PERIOD^:= FPWM_PERIOD^ or (Value shl 16);
end;

procedure TPWM.SetDuty(Value: Word);
var D: LongWord;
begin
  if (FCycle > 0) and (Value >= FCycle) then FDuty:= FCycle - 1;
  FPWM_PERIOD^:= FPWM_PERIOD^ and $FFFF0000;
  FPWM_PERIOD^:= FPWM_PERIOD^ or Value;
end;

procedure TPWM.Start;
begin
  with TPWMGROUP.Instance do
  FPWM_CTRL^:= FPWM_CTRL^ or ($1 shl (Ord(FChannel) * 15 + 4));
end;

procedure TPWM.Stop;
begin
  with TPWMGROUP.Instance do
  FPWM_CTRL^:= FPWM_CTRL^ and not ($1 shl (Ord(FChannel) * 15 + 4));
end;


finalization
  TPWMGROUP.Instance.Release;

end.