<?php


class MiMic_LPC1769_Pwm
{
	/*
		public class Option
		{
		public UInt32? freq;
		public Peripheral.Option phl;
		public Option(Option s)
		{
		if(s!=null){
		$this->freq=s.freq;
		$this->phl=new Peripheral.Option(s.phl);
		}
		}
		public Option(UInt32 i_freq,Peripheral.Option i_phl)
		{
		$this->freq=i_freq;
		$this->phl=i_phl;
		}
		}*/
	const _TCR=0x40018004;
	const _TC=0x40018008;
	const _PR=0x4001800C;
	const _PC=0x40018010;
	const _MCR=0x40018018;
	const _PCR=0x4001804C;
	const _LER=0x40018050;
	const _CTCR=0x40018070;
	private static $_MRn=array(0x40018018,0x4001801C,0x40018020,0x40018024,0x40018040,0x40018044,0x40018048);
	public $_mcu;
	private $_phl;
	private $_cache_mr0;

	public function __construct($i_mcu,$i_opt=null)
	{
		$this->_mcu=$i_mcu;
		//PHL生成。
		$this->_phl=new MiMic_LPC1769_Peripheral($i_mcu,MiMic_LPC1769::$PHL['PWM1'],null);

		//設定値のロード
		$opt=is_null($i_opt)?array():$i_opt;
		if(!isset($opt['phl'])){
			$opt['phl']=array();
		}

		//デフォルト値設定
		if(!isset($opt['phl']['power'])){$opt['phl']['power']=1;};
		if(!isset($opt['phl']['clock'])){$opt['phl']['clock']=0;};
		if(!isset($opt['freq'])){$opt['freq']=100;}

			

		$bc="";
		$db=array();

		//optionの設定
		$bc.=$this->BCF_setOpt($opt,$db);
		$bc.=MiMic_LPC1769::BCF_setMem(self::_PC,0x0,$db);

		//PCR
		$bc.=MiMic_LPC1769::BCF_setMem(self::_PCR,0x0,$db);
		//CTCR
		$bc.=MiMic_LPC1769::BCF_setMem(self::_CTCR,0x0,$db);
		//TCRに値設定(カウンタとPWMモードを無効化)
		$bc.=MiMic_LPC1769::BCF_setMem(self::_TCR,0x0,$db);
		$bc.=MiMic_LPC1769::BCF_setMem(self::_TC,0x0,$db);
		//TCRに値設定(カウンタとPWMモードを有効化)
		$bc.=MiMic_LPC1769::BCF_setMem(self::_TCR,0x9,$db);

		//初期化

		$this->_mcu->callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db);

		//ペリフェラルをMCUに登録

		$this->_mcu->registerPhl($this,"PWM");

	}

	function BCF_setOpt($i_opt,&$i_db)
	{
		$bc="";

		//ペリフェラルの設定
		if(isset($i_opt['phl']))
		{
			$bc.=$this->_phl->BCF_setOpt($i_opt['phl'],$i_db);
		}

		if(isset($i_opt['freq'])){
			//サイクル(Hz)とペリフェラルのクロックから、MR0のサイクルを決定
			$mr0=($this->_phl->getPCLK()/$i_opt['freq']);
			if($mr0<=0){
				//ペリフェラルクロック小さすぎﾜﾛﾀ
				throw new MiMicException("The peripheral clock too small.");
			}
			//MR0に値設定
			$bc.=$this->BCF_setMRn(0,$mr0,$i_db);
			//LERにビットセット
			$bc.=$this->BCF_setLER(0,$i_db);
			$this->_cache_mr0=$mr0;
		}
		return $bc;
	}

	function BCF_setLER($i_ch,&$i_db)
	{
		//LERにビットをセット
		return MiMic_LPC1769::BCF_setBit(self::_LER,0x01,0x01,$i_ch,$i_db);
	}

	function BCF_setLERs($i_mask,$i_value,&$i_db)
	{
		//LERにビットをセット
		return MiMic_LPC1769::BCF_setBit(self::_LER,$i_mask,$i_value*$i_mask,0,$i_db);
	}

	function BCF_setMRn($i_ch,$i_val,&$i_db)
	{
		return MiMic_LPC1769::BCF_setMem(self::$_MRn[$i_ch],$i_val,$i_db);
	}

	function BCF_setMRnByDuty($i_ch,$i_duty,&$i_db)
	{
		return $this->BCF_setMRn($i_ch,($i_duty*$this->_cache_mr0),$i_db);
	}

	function BCF_setPCRbits($i_mask,$i_edge,$i_en,&$i_db)
	{
		$m=$v=0;
		if(isset($i_edge)){
			$t=0xff&$i_mask;
			$v|=$i_edge*$t;
			$m|=$t;
		}
		if(isset($i_en)){
			$t=0xff00&$i_mask;
			$v|=($i_en*$t);
			$m|=$t;
		}
		//ビットの設定
		return MiMic_LPC1769::BCF_setBit(self::_PCR,$m,$v,0,$i_db);
	}

	public function setOpt($i_opt)
	{
		$db=array();
		$bc=$this->BCF_setOpt($i_opt,$db);
		$this->_mcu->callMiMicWithCheck($bc.LPC1769::_BCF_END,$db);
	}
	public function getPin($i_pin,$i_opt)
	{
		return new MiMic_LPC1769_PwmPin($this,$i_pin,$i_opt);
	}

	public function getPort($i_pins,$i_opt)
	{
		return new MiMic_LPC1769_PwmPort($this,$i_pins,$i_opt);
	}
}

class MiMic_LPC1769_PwmPort
{
	private static function pin2PwmPinInfo($i_pin)
	{
		//pinの完全な機能名を得る。(得られれば機能がある。)
		$fn=MiMic_LPC1769::completePinFunctionName($i_pin,'PWM');
		//portとbitを得る(AD0だけしか管理しないよ)
		$a=preg_split("/[\s.]+/",substr($fn,2));
		return array('port'=>0,'ch'=>intval($a[1]),'pin_sel'=>MiMic_LPC1769::getPinSelByFunctionName($i_pin,$fn));
	}
	protected $_pwm;
	private $_pins;
	const _PINSEL_AUTO_DETECT=0x0fffffff;
	private $_port_no;
	private $_port;
	private $_pcr_mask;
	private $_ler_mask;
	public function __construct($i_pwm,$i_pins,$i_opt=null)
	{
		$this->_pwm=$i_pwm;
		//ピンセットを取得
		$this->_pins=array();
		//pinに変換する。
		for($i=0;$i<count($i_pins);$i++){
			$this->_pins[$i]=self::pin2PwmPinInfo($i_pins[$i]);
		}

		//pinが全て同じポートに所属しているか確認
		$p=$this->_pins[0]['port'];
		for($i=1;$i<count($this->_pins);$i++){
			if($p!=$this->_pins[$i]['port']){
				throw new MiMicException("Invalid pin combination.");
			}
		}
		//ポートの生成
		$this->_port=new MiMic_LPC1769_Port($i_pwm->_mcu,$i_pins,null);
		$this->_port_no=$p;

		$this->_ler_mask=0;
		$this->_pcr_mask=0;
		for($i=0;$i<count($this->_pins);$i++){
			$this->_ler_mask|=(0x1<<$this->_pins[$i]['ch']);
			$this->_pcr_mask|=(0x101<<$this->_pins[$i]['ch']);
		}
		$opt=$i_opt;
		if(!isset($opt['pin'])){
			$opt['pin']=array();
		}

		//デフォルト値のロード

		if(!isset($opt['pin']['sel'])){$opt['pin']['sel']=self::_PINSEL_AUTO_DETECT;}
		if(!isset($opt['duty'])){$opt['duty']=0;}
		if(!isset($opt['enable'])){$opt['enable']=1;}
		$this->setOpt($opt);
	}

	public function setOpt($i_opt)
	{
		$db=array();
		//BCの生成
		$bc="";

		//i_optの展開
		if(isset($i_opt['pin']))
		{
			$optset=array();
			for($i=0;$i<count($this->_pins);$i++){
				//pinselが_PINSEL_AUTO_DETECTならばauto。そうでなければundefinedも含めて設定
				$s=($i_opt['pin']['sel']==self::_PINSEL_AUTO_DETECT)?$this->_pins[$i]['pin_sel']:$i_opt['pin']['sel'];
				$optset[$i]=array('sel'=>$s,'mode'=>$i_opt['pin']['mode'],'od'=>$i_opt['pin']['od']);
			}
			//portの設定
			$bc.=$this->_port->BCF_setOpts($optset,$db);
		}

		//PCRに値設定

		if(isset($i_opt['enable'])){
			$bc.=$this->_pwm->BCF_setPCRbits($this->_pcr_mask,0,$i_opt['enable'],$db);
		}
		//デューティ比を
		if(isset($i_opt['duty'])){
			//デューティ比を設定
			for($i=0;$i<count($this->_pins);$i++){
				$bc.=$this->_pwm->BCF_setMRnByDuty($this->_pins[$i]['ch'],$i_opt['duty'],$db);
			}
			//LERにセット
			$bc.=$this->_pwm->BCF_setLERs($this->_ler_mask,1,$db);
		}
		$this->_pwm->_mcu->callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db);
		return;
	}

	//
	//setDutys
	//
	private function BCF_setDutys($i_duty_array,&$i_db)
	{
		if(count($i_duty_array)!=count($this->_pins)){
			throw new MiMicException();
		}

		$bc="";
		//デューティ比をまとめてセット
		for($i=0;$i<count($this->_pins);$i++){
			if($i_duty_array[$i]!=null){
				$bc.=$this->_pwm->BCF_setMRnByDuty($this->_pins[$i]['ch'],$i_duty_array[$i],$i_db);
			}
		}
		//LERにセット
		$bc.=$this->_pwm->BCF_setLERs($this->_ler_mask,1,$i_db);
		return $bc;
	}

	public function setDutys($i_duty_array)
	{
		$db=array();
		$bc=$this->BCF_setDutys($i_duty_array,$db);
		$this->_pwm->_mcu->callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db);
	}



}

class MiMic_LPC1769_PwmPin
{
	private $_pport;
	public function __construct($i_pwm,$i_pin,$i_opt=null)
	{
		//1pinのポートとして実装
		$this->_pport=new MiMic_LPC1769_PwmPort($i_pwm,array($i_pin),$i_opt);
	}

	public function setOpt($i_opt)
	{
		$this->_pport->setOpt($i_opt);
	}
	public function setDuty($i_duty)
	{
		$this->_pport->setDutys(array($i_duty));
	}
}

?>