/**
 * @fileOverview LPC1769の、ADコンバータペリフェラル、ピンの制御クラスを定義する。
 */
(function(){
var DEV=LPC1769;
var BCF=DEV._BCF;
var EE=DEV._EE;
var isUndef=MiMicLib.isUndef;
var cloneAssoc=MiMicLib.cloneAssoc;


/**
 * LPCXPresso1769.Adc (Adc)クラスのコンストラクタ。
 * MCUに関連付けしたADペリフェラルを生成する。
 * ADペリフェラルは、MCUのADCペリフェラル全体を管理する。
 * 関数は、ADCRレジスタのPDN,BURSTを1にセットする。
 * ADCペリフェラルはバーストモードで動作し、サンプリングレートは200KHz固定である。取得タイミングの制御はハードウェア依存である。
 * @constructor
 * @name LPC1769.Adc
 * @param {object as LPC1769.Mcu} i_mcu
 * インスタンスを結びつけるMcuオブジェクト。
 * @param {object as associative array} i_opt
 * インスタンス生成と同時にsetOpt関数で設定する値。省略時は、{phl:{power:1}}とみなす。
 * 詳細はsetOpt関数を参照。 
 * @example
 * //create AD (logical)pheripheral
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var ad=new LPC1769.Adc(mcu); 
 */
DEV.Adc=function Adc(i_mcu,i_opt)
{
	try{
		this._mcu=i_mcu;
		//PHL生成。
		this._phl=new DEV.Peripheral(i_mcu,DEV.PHL.ADC);
		//設定値のロード
		var opt=isUndef(i_opt)?{phl:{}}:
		{//i_optある。
			phl:isUndef(i_opt.phl)?{}:cloneAssoc(i_opt.phl),
		};
		//デフォルト値設定
		if(isUndef(opt.phl.power)){opt.phl.power=1;};

		//初期化。
		var bc="";
		var db=new Array();
		bc+=this.BCF_setOpt(opt,db);
		//0x00210000(PDN,BURSTを1)
		bc+=BCF.setMem(this._AD0CR,0x00200400,db);
		bc+=BCF.setMem(this._AD0CR,0x00210400,db);
		//
		this._mcu.callMiMicWithCheck(bc+BCF.END,db);
		//ペリフェラルをMCUに登録
		this._mcu.registerPhl(this,"ADC");
	}catch(e){
		throw new MiMicException(e);
	}

}
DEV.Adc.prototype=
{
	_AD0CR:0x40034000,
	_AD0DR:[0x40034010,0x40034014,0x40034018,0x4003401C,0x40034020,0x40034024,0x40034028,0x4003402C],
	_phl:null,
	_mcu:null,

	BCF_setOpt:function BCF_setOpt(i_opt,i_db)
	{
		try{
			var bc="";
			if(!isUndef(i_opt.phl)){
				bc+=this._phl.BCF_setOpt(i_opt.phl,i_db);
			}
			return bc;
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * AD0CRのSELフィールドの値を更新するBC
	 @param i_val
	 0 or 1
	 @private
	 */
	BCF_setSels:function BCF_setSels(i_mask,i_val,i_db)
	{
		try{
			return BCF.setBit(this._AD0CR,i_mask,i_val*i_mask,0,i_db);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * n番のAD0DRの値を得るBC
	 @private
	 */
	BCF_getAD0DR:function BCF_getAD0DR(i_ch,i_db)
	{
		try{
			return BCF.getMem(this._AD0DR[i_ch],i_db);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ADCペリフェラルに、i_optのオプション値を設定する。
	 * @name LPC1769.Adc#setOpt
	 * @function
	 * @param {object as associative array} i_opt
	 * ADCペリフェラルのコンフィグレーションパラメタである。必要な値を格納した連想配列で指定する。
	 * 全ての値を省略することは出来ない。連想配列のメンバは以下の通り。
	 * <pre>{phl:object as associative array}</pre>
	 * <ul>
	 * <li>phl - LPC1769.Peripheral#setOpt関数のi_optに渡すパラメタである。</li>
	 * </ul>
	 * @example
	 * //AD power on/off
	 * //create AD (logical)pheripheral
	 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
	 * var ad=new LPC1769.Adc(mcu);
	 * ad.setOpt(phl:{power:0});
	 */
	setOpt:function setOpt(i_opt)
	{
		try{
			var db=new Array();
			var bc=this.BCF_setOpt(i_opt,db);
			this._mcu.callMiMicWithCheck(bc+BCF.END,db);
		}catch(e){
			throw new MiMicException(e);
		}
	},	
	/**
	 * AD機能を持つピンを取得する。
	 * ピン識別子で指定されるピンをADペリフェラルと結合して、AdcPinを生成する。
	 * 関数は、AdcPinオブジェクトのコンストラクタをコールして、AdcPinを生成する。失敗すると、例外をスローする。
	 * 生成ルールについての詳細は、AdcPinを参照。
	 * @name LPC1769.Adc#getPin
	 * @function
	 * @param {object as ピン識別子} i_pin
	 * AD機能を割り当てるPINの識別子である。値は、LPC1769.Pn[m]のメンバ変数である。
	 * @param {object as associative array} i_opt
	 * AdcPinのコンストラクタに渡すオプション値。省略時はundefinedである。詳細はLPCXpresso1769.AdcPin.setOptを参照。
	 * @return {object as LPC1769.AdcPin}
	 * LPC1769.AdcPinクラスのオブジェクトである。
	 * @example
	 * //create AdcPin
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var adc=new LPC1769.Adc(mcu);
	 * var adpin=adc.getPin(LPC1769.P0[23]);
	 */
	getPin:function getPin(i_pin,i_opt)
	{
		try{
			return new DEV.AdcPin(this,i_pin,i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * AD機能を持つポート(Pin集合)を取得する。
	 * ピン識別子で指定されるピンのセットをADペリフェラルと結合して、AdcPortを生成する。
	 * 関数は、AdcPortのコンストラクタをコールする。
	 * 生成ルールについては、AdcPort関数を参照すること。
	 * @name LPC1769.Adc#getPort
	 * @function
	 * @param {array[ピン識別子]} i_pin
	 * AD機能を割り当てるPINの識別子の配列である。値は、LPC1769.Pn[m]のメンバ変数である。
	 * @param {object as associative array} i_opt
	 * AdcPortのコンストラクタに渡すオプション値。省略時はundefinedである。詳細はLPCXpresso1769.AdcPort.setOptを参照。
	 * @return {object as LPC1769.AdcPort}
	 * LPC1769.AdcPortクラスのオブジェクトである。
	 * @example
	 * //create 2AdcPort that has 2 pins.
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var adc=new LPC1769.Adc(mcu);
	 * var port=adc.getPort([LPC1769.P0[23],LPC1769.P0[24]]);
	 */
	getPort:function getPort(i_pins,i_opt)
	{
		try{
			return new DEV.AdcPort(this,i_pins,i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	}
}


/**
 * AD pinからADInfoを取得
 * @private
 */
function pin2AdcPinInfo(i_pin)
{
	try{
		//pinの完全な機能名を得る。(得られれば機能がある。)
		var func_name=DEV.completePinFunctionName(i_pin,"AD");
		//portとbitを得る(AD0だけしか管理しないよ)
		var a=func_name.substring(2).split(".");
		var r={port:0,ch:parseInt(a[1]),pin_sel:DEV.getPinSelByFunctionName(i_pin,func_name)};
		if(!isNaN(r.ch)){
			return r;
		}
		throw new MiMicException(EE.INVALID_CFG,"The pin has not AD fuction.");
	}catch(e){
		throw new MiMicException(e);	
	}
}
/**
 * LPC1769.AdcPort (AdcPort)クラスのコンストラクタ。複数のADピンから一括で値を取得するときに使用する。
 * Adcペリフェラルオブジェクトにピン識別子の配列で指定されたピン集合を関連付けて、AD機能を持つポートを生成する。
 * 関数は、ピン識別子を元に、そのピンがAD機能に接続できるかを調べる。全てのピンにAD機能を割り当てられない場合、例外が発生する。どのピンにAD機能が割り当てられるかは、MCUのスペックシートを参照すること。
 * @constructor
 * @name LPC1769.AdcPort
 * @param {object as LPC1769.Adc} i_adc
 * インスタンスを結びつけるAdcオブジェクト。
 * @param {array[pin識別子]} i_pins
 * ピン識別子の配列。指定できるのは、LPC1769.P?[?]である。順番は、このインスタンスの返す値の順序に影響する。
 * @param {object as associative array} i_opt
 * setOpt関数のi_optに渡すパラメタである。省略可能。省略時は{pin:{sel:auto}}を設定する。
 * autoは、関数が自動的に決定するPINSELの値である。詳細はsetOpt関数を参照。 
 * @example
 * //create [AD0.0,AD0.1]
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var adc=new LPC1769.Adc(mcu);
 * var port=new LPC1769.AdcPort(adc,[LPC1769.P0[23],LPC1769.P0[24]]); 
 */
DEV.AdcPort=function AdcPort(i_adc,i_pins,i_opt)
{
	try{
		this._adc=i_adc;
		//ピンセットを取得
		this._pins=new Array();
		//pinに変換する。
		for(var i=0;i<i_pins.length;i++){
			this._pins.push(pin2AdcPinInfo(i_pins[i]));
		}
		//pinが全て同じポートに所属しているか確認
		var p=this._pins[0].port;
		for(var i=1;i<this._pins.length;i++){
			if(p!=this._pins[i].port){
				throw new MiMicException("Invalid pin combination.");
			}
		}
		//ポートの生成
		this._port=new DEV.Port(i_adc._mcu,i_pins);
		this._port_no=p;
		//AD0CR用のマスクを生成。
		this._adcr_mask=0;
		for(var i=0;i<this._pins.length;i++){
			this._adcr_mask|=(0x1<<this._pins[i].ch);
		}
		//ピンオプションの生成
		var opt=isUndef(i_opt)?{}:cloneAssoc(i_opt);
		opt.sel=1;//ADxCRの値
		opt.pin=isUndef(opt.pin)?{}:cloneAssoc(opt.pin);
		//設定が無ければ、ピンセレクタを自動に設定
		if(isUndef(opt.pin.sel)){opt.pin.sel=this._PINSEL_AUTO_DETECT;}
		//ピンオプションの設定
		this.setOpt(opt);
	}catch(e){
		throw new MiMicException(e);
	}	
}
DEV.AdcPort.prototype=
{
	_PINSEL_AUTO_DETECT:0x0fffffff,
	_adc:null,
	_pins:null,
	_port_no:0,
	_port:null,
	_adcr_mask:0,
	/**
	 * ADポートにオプション値を設定する。
	 *　関数は、ポートを構成する全てのピンに、同一なオプション値を設定する。
	 * 設定可能な値は、LPC1769.AdcPin#setOptと同じである。
	 * @name LPC1769.AdcPort#setOpt
	 * @function
	 * @param {object as associative array} i_opt
	 * LPC1769.AdcPin#setOptを参照。
	 */	
	setOpt:function setOpt(i_opt)
	{
		try{
			var db=new Array();
			//BCの生成
			var bc="";
			//i_optの展開
			if(!isUndef(i_opt.pin)){
				var optset=new Array();
				for(var i=0;i<this._pins.length;i++){
					//pinselが_PINSEL_AUTO_DETECTならばauto。そうでなければundefinedも含めて設定
					var s=(i_opt.pin.sel==this._PINSEL_AUTO_DETECT)?this._pins[i].pin_sel:i_opt.pin.sel;
					optset.push({sel:s,mode:i_opt.pin.mode,od:i_opt.pin.od});
				}
				//portの設定
				bc+=this._port.BCF_setOpts(optset,db);
			}
			//隠し。ADxCR
			if(!isUndef(i_opt.sel)){
				bc+=this._adc.BCF_setSels(this._adcr_mask,i_opt.sel,db);
			}
			this._adc._mcu.callMiMicWithCheck(bc+BCF.END,db);
			return;
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ポートからAD変換した値を得る。
	 * @name LPC1769.AdcPort#getValues
	 * @function
	 * @return {array[int]}
	 * 12bitのAD変換値の配列である。値の意味は、UM10360 Chapter 29: LPC17xx Analog-to-Digital Converter (ADC)を参照。
	 * 値の順番は、コンストラクタで指定したピン配列の順序と同じである。
	 * @example
	 * //show value of AD0.0,AD0.1 pin
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var pin=mcu.getPort("AD",[LPC1769.P0[23],LPC1769.P0[24]]);
	 * alert(pin.getValues());
	 */	
	getValues:function getValues()
	{
		try{
			//メモリから値取得
			var db=new Array();
			var bc="";
			for(var i=0;i<this._pins.length;i++){
				bc+=this._adc.BCF_getAD0DR(this._pins[i].ch,db);
			}
			var ret=this._adc._mcu.callMiMicWithCheck(bc+BCF.END,db);
			//値の整形
			var r=new Array();
			for(var i=0;i<this._pins.length;i++){
				r.push((ret.stream[i]>>4)&0x00000fff);
			}
			return r;
		}catch(e){
			throw new MiMicException(e);
		}
	}
}
/**
 * LPC1769.AdcPin (AdcPin)クラスのコンストラクタ。
 * Adcペリフェラルオブジェクトにピン識別子で指定されたピンを関連付けて、AD機能を持つピンを生成する。
 * 関数は、ピン識別子を元に、そのピンがAD機能に接続できるかを調べる。ピンにAD機能を割り当てられない場合、例外が発生する。どのピンにAD機能が割り当てられるかは、MCUのスペックシートを参照すること。
 * @constructor
 * @name LPC1769.AdcPin
 * @param {object as LPC1769.Adc} i_adc
 * インスタンスを結びつけるAdcオブジェクト。
 * @param {object as pin識別子} i_pin
 * ピン識別子。指定できるのは、LPCXpresso1796.P?[?]である。
 * @param {object as associative array} i_opt
 * setOpt関数のi_optに渡すパラメタである。省略可能。省略時は{pin:{sel:auto}}を設定する。
 * autoは、関数が自動的に決定するPINSELの値である。詳細はsetOpt関数を参照。 
 * @example
 * //create AD0.0
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var adc=new LPC1769.Adc(mcu);
 * var adcpin=new  LPC1769.AdcPin(adc,LPC1769.P0[23]); 
 */
DEV.AdcPin=function AdcPin(i_adc,i_pin,i_opt)
{
	try{
		this._aport=new DEV.AdcPort(i_adc,[i_pin],i_opt);
	}catch(e){
		throw new MiMicException(e);
	}
}

DEV.AdcPin.prototype=
{
	_aport:null,
	/**
	 * ピンからAD変換した値を得る。
	 * @name LPC1769.AdcPin#getValue
	 * @function
	 * @return {int}
	 * 12bitのAD変換値を返す。値の意味は、UM10360 Chapter 29: LPC17xx Analog-to-Digital Converter (ADC)を参照。
	 * @example
	 * //show value of AD0.0 pin
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var pin=mcu.getPin("AD0.0");
	 * alert(pin.getValue());	 
	 */
	getValue:function getValue()
	{
		try{
			return this._aport.getValues()[0];
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ADピンにオプション値を設定する。
	 * @name LPC1769.AdcPin#setOpt
	 * @function
	 * @param {object as associative array} i_opt
	 * ADピンのコンフィグレーションパラメタである。必要な値を格納した連想配列で指定する。
	 * 全ての値を省略することは出来ない。連想配列のメンバは以下の通り。
	 * <pre>{pin:object as associative array}</pre>
	 * <ul>
	 * <li>pin - LPC1769.Pin#setOpt関数のi_optに渡すパラメタである。</li>
	 * </ul>
	 */	
	setOpt:function setOpt(i_opt)
	{
		try{
			this._aport.setOpt(i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	}
}
	


}());
