<?php
class MiMic_LPC1769_Gpio
{
	public function __construct($i_mcu,$i_opt=null)
	{
		$this->_ref_mcu=$i_mcu;
		$i_mcu->registerPhl($this,"GPIO");
	}
	private static $_FIO_DIR=array(0x2009C000,0x2009C020,0x2009C040,0x2009C060,0x2009C080);
	private static $_FIO_PIN=array(0x2009C014,0x2009C034,0x2009C054,0x2009C074,0x2009C094);
	private static $_FIO_SET=array(0x2009C018,0x2009C038,0x2009C058,0x2009C078,0x2009C098);
	private static $_FIO_CLR=array(0x2009C01C,0x2009C03C,0x2009C05C,0x2009C07C,0x2009C09C);
	private static $_FIO_MASK=array(0x2009C010,0x2009C030,0x2009C050,0x2009C070,0x2009C090);
	public $_ref_mcu;
	public function BCF_getValues($i_ch,$i_mask,&$i_db)
	{
		/*GPIOレジスタからとってくるBC
			SGET #0;//GPIOの値レジスタアドレス
			SGET #1;//GPIOのMASKレジスタアドレス
			SGET #2;//MASKレジスタの値
			MSET #2,#1;//MASK設定
			MGET #3,#0;//値取得
			SPUT #3;//SPUT
			*/
		array_push($i_db,self::$_FIO_PIN[$i_ch]);
		array_push($i_db,self::$_FIO_MASK[$i_ch]);
		array_push($i_db,~$i_mask);
		return 'EA00EA01EA02DF0201DB0300EE03';
	}
	/**
	 FIOPINとFIOMASKに値をセットする。
	 SGET #0;//GPIOの値レジスタアドレス
	 SGET #1;//GPIOのMASKレジスタアドレス
	 SGET #2;//MASKレジスタの値
	 SGET #3;//GPIO値
	 MPUT #2,#1;//MASK設定
	 MPUT #3,#0;//値設定
		* @private
		*/
	public function BCF_setValues($i_ch,$i_mask,$i_value,&$i_db)
	{
		array_push($i_db,self::$_FIO_PIN[$i_ch]);
		array_push($i_db,self::$_FIO_MASK[$i_ch]);
		array_push($i_db,~$i_mask);
		array_push($i_db,$i_value);
		return 'EA00EA01EA02EA03DF0201DF0300';
	}
	/**
	 *
	 @param i_dir 1 or 0
	 @private
	 */
	public function BCF_setDirs($i_ch,$i_mask,$i_dir,&$i_db)
	{
		return MiMic_LPC1769::BCF_setBit(self::$_FIO_DIR[$i_ch],$i_mask,$i_mask*$i_dir,0,$i_db);
	}
	public function getPin($i_pin,$i_opt=null)
	{
		return new MiMic_LPC1769_GpioPin($this,$i_pin,$i_opt);
	}
	public function getPort($i_pins,$i_opt=null)
	{
		return new MiMic_LPC1769_GpioPort($this,$i_pins,$i_opt);
	}
}

class MiMic_LPC1769_GpioPort
{
	private static function pin2GpioPinInfo($i_pin)
	{
		//pinの完全な機能名を得る。(得られれば機能がある。)
		$fn=MiMic_LPC1769::completePinFunctionName($i_pin,"GPIO");
		//pin名からポートとビットを得る。
		$a=preg_split("/[\s.]+/",substr($fn,4));	
		//pin情報を構成。
		return array('port'=>intval($a[0]),'bit'=>intval($a[1]),'pin_sel'=>MiMic_LPC1769::getPinSelByFunctionName($i_pin,$fn));
	}
	private $_port;
	private $_gpio;
	private $_pins;
	private $_mask;
	private $_port_no;
	const _PINSEL_AUTO_DETECT=0x0fffffff;

	public function __construct($i_gpio,$i_pins,$i_opt=null)
	{
		$this->_gpio=$i_gpio;
		//ピンセットを取得
		$this->_pins=array();
		//pinに変換する。
		for($i=0;$i<count($i_pins);$i++){
			array_push($this->_pins,self::pin2GpioPinInfo($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_gpio->_ref_mcu,$i_pins,null);
		$this->_port_no=$p;
		//GPIO用のマスクを生成。
		$this->_mask=0;
		for($i=0;$i<count($this->_pins);$i++){
			$this->_mask|=(0x1<<$this->_pins[$i]['bit']);
		}

		//ピンオプションの生成(複製と整形)
		$opt=is_null($i_opt)?array():$i_opt;
		//ピンプロパティの複製
		$opt['pin']=isset($opt['pin'])?$opt['pin']:array();
		//設定が無ければ、ピンセレクタを自動に設定
		if(!isset($opt['pin']['sel'])){$opt['pin']['sel']=self::_PINSEL_AUTO_DETECT;}
			
		//ピンオプションの設定

		$this->setOpt($opt);
		
	}

	public function setOpt($i_opt)
	{
		if(!isset($i_opt)){
			throw new MiMicException();
		}
		$db=array();
		//BCの生成
		$bc="";
		//dir設定
		if($i_opt['dir']!=null){
			$bc.=$this->_gpio->BCF_setDirs($this->_port_no,$this->_mask,$i_opt['dir'],$db);
		}
		//i_optの展開		
		if($i_opt['pin']!=null){
			$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'];
				array_push($optset,array('sel'=>$s,'mode'=>$i_opt['pin']['mode'],'od'=>$i_opt['pin']['od']));
			}
		
			$bc.=$this->_port->BCF_setOpts($optset,$db);
		}
		//portの設定
		$this->_gpio->_ref_mcu->callMiMicWithCheck($bc. MiMic_LPC1769::_BCF_END,$db);
		return;
	}

	//
	//setValue
	//
	private function BCF_setValue($i_val,&$i_db)
	{
		$v=0;
		$l=count($this->_pins);
		for($i=0;$i<$l;$i++){
			//セットするbit値を得る。
			$sv=(($i_val>>$i)&0x01);
			//値のセット
			$v|=($sv<<$this->_pins[$i]['bit']);
		}
		//値をセット
		return $this->_gpio->BCF_setValues($this->_port_no,$this->_mask,$v,$i_db);
	}
	public function setValue($i_val)
	{
		$db=array();
		$bc=$this->BCF_setValue($i_val,$db);
		$this->_gpio->_ref_mcu->callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db);
	}

	protected function BCF_getValue(&$i_db)
	{
		return $this->_gpio->BCF_getValues($this->_port_no,$this->_mask,$i_db);
	}
	private function PARSE_getValue($i_result)
	{
		//値の再構成
		$retval=$i_result->stream[0];
		$v=0;
		for($i=count($this->_pins)-1;$i>=0;$i--){
			//セットするbit値を得る。
			$sv=(($retval>>$this->_pins[$i]['bit'])&0x1);
			//値のセット(pinArrayの並びとビット並びが同じになるようにする)
			$v=($v<<1)|$sv;
		}
		return $v;
	}

	public function getValue()
	{
		$db=array();
		$bc=$this->BCF_getValue($db);
		return $this->PARSE_getValue($this->_gpio->_ref_mcu->callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db));
	}


	//
	//outPatt
	//
	protected function BCF_outPatt($i_val_array,&$i_db)
	{
		$bc="";
		for($i2=0;$i2<count($i_val_array);$i2++){
			$v=0;
			$l=count($this->_pins);
			for($i=0;$i<$l;$i++){
				//セットするbit値を得る。
				$sv=(($i_val_array[$i2]>>$i)&0x01);
				//値のセット(pinArrayの並びと最下位ビットの位置が同じになるように反転)
				$v|=($sv<<$_pins[i]['bit']);
			}
			//値をセット
			$bc.=$this->_gpio->BCF_setValues($this->_port_no,$this->_mask,$v,$i_db);
		}
		return $bc;
	}

	public function outPatt($i_val_array)
	{
		$db=array();
		$bc=$this->BCF_outPatt($i_val_array,$db);
		$this->_gpio->_ref_mcu.callMiMicWithCheck($bc.MiMic_LPC1769::_BCF_END,$db);
	}
}


class MiMic_LPC1769_GpioPin
{
	protected $_gport;
	public function __construct($i_gpio,$i_pin,$i_opt=null)
	{
		//1ピンのポート
		$this->_gport=new MiMic_LPC1769_GpioPort($i_gpio,array($i_pin),$i_opt);
	}
	public function setOpt($i_opt)
	{
		$this->_gport->setOpt($i_opt);
	}
	public function setValue($i_val)
	{
		$this->_gport->setValue($i_val);
	}
	public function getValue()
	{
		return $this->_gport->getValue();
	}
	public function outPatt($i_val_array)
	{
		$this->_gport->outPatt($i_val_array);
	}
}




?>