/* ======================================================== 藤田@開技 nintendo '09 Apr ======================================================== */ #include "incs.h" #include "adc.h" #include "pm.h" #include "led.h" bit adc_updated; u8 adc_raw_vol; u8 adc_raw_dep; #define INTERVAL_TSK_ADC 3 /* ======================================================== ADC設定と、開始 以下のピンは主にここで操作・監視されます。 ・BT_TEMP,_P ・ADIN1 ・VOL 関係ありそうですが別のところで管理しています ・PM_BT_DET,_P PM_init ・8tics毎に呼ばれ、3チャンネル分取り込むとADCを停止します。  タスク起動時、レジスタには前回の取り込み値が入っています。 ======================================================== */ /* // max -4db static const u8 slider_to_codec[64] = { 127, 125, 124, 123, 121, 120, 119, 117, 116, 115, 113, 112, 111, 109, 108, 107, 105, 104, 103, 101, 100, 99, 98, 96, 95, 94, 92, 91, 90, 88, 87, 86, 84, 83, 82, 80, 79, 78, 76, 75, 74, 72, 71, 70, 69, 67, 66, 65, 63, 62, 61, 59, 58, 57, 55, 54, 53, 51, 50, 49, 47, 46, 45, 44 }; */ // max -10db static const u8 slider_to_codec[64] = { 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66 }; void tsk_adc( ) { static u8 task_interval = 0; static u8 old_tune; static u8 sndvol_codec; static u8 bt_temp_old; if( task_interval-- != 0 ) { return; } else { task_interval = (u8)( INTERVAL_TSK_ADC / SYS_INTERVAL_TICK ); } if( adc_updated ) { if( system_status.pwr_state == ON ) { // Tune /////////////////////////////////////// { // 似非ヒステリシス V2 // ガリオームには適さない #define KIKAN 32 static u8 old_value; static s8 diffs; u8 temp; if( abs( adc_raw_dep - old_value ) >= 2 ) { // 大きく離れた vreg_ctr[ VREG_C_TUNE ] = adc_raw_dep; old_value = adc_raw_dep; #if 0 割り込み入れない; 割り込みを入れるようであれば、ちゃんと変化チェックする; set_irq( VREG_C_IRQ0, REG_BIT_VR_TUNE_CHANGE ); #endif diffs = 0; } else { // 近所の値でも、ある期間でいっぱい偏っていたらそっちに寄せる static u8 kikan_count = KIKAN; if( old_value < adc_raw_dep ) { diffs += 1; } else if( old_value > adc_raw_dep ) { diffs -= 1; } if( --kikan_count == 0 ) { if( diffs >= KIKAN && ( diffs < 64 )) { old_value += 1; } else if( ( diffs <= ( 256 - KIKAN )) && ( diffs > ( 128 + 64 ) )) // あらー? { old_value -= 1; } vreg_ctr[ VREG_C_TUNE ] = old_value; kikan_count = KIKAN; diffs = 0; } } } vreg_ctr[ VREG_C_DBG1 ] = vreg_ctr[ VREG_C_TUNE ]; vreg_ctr[ VREG_C_DBG2 ] = adc_raw_dep; // dbg // Volume ///////////////////////////////////// { // 似非ヒステリシスを付けて64段 u8 temp; static u8 vol_old; static u8 force_update_vol; if( abs( adc_raw_vol - vol_old ) >= 2 ) // 生値でこれくらいずれたら更新 { // if( vreg_ctr[ VREG_C_SND_VOL ] != ( adc_raw_vol / 4 ) ) { vol_old = adc_raw_vol; // レジスタ更新 vreg_ctr[ VREG_C_SND_VOL ] = ( adc_raw_vol / 4 ); vreg_twl[ REG_TWL_INT_ADRS_VOL ] = adc_raw_vol / ( 256 / 32 ); // ←adc値でよい // codecに伝える iic_mcu_write_a_byte( IIC_SLA_CODEC, CODEC_REG_VOL, slider_to_codec[ adc_raw_vol / 4 ] ); #ifndef _MODEL_CTR_ iic_mcu_write_a_byte( IIC_SLA_DCP, 0, slider_to_codec[ ( 255 - adc_raw_vol ) / 4 ] ); // todo #endif // set_irq( VREG_C_IRQ0, REG_BIT_VR_SNDVOL_CHANGE ); // 割り込み廃止 force_update_vol = 100; } } { // ポーリング if( --force_update_vol == 0 ) { vol_old = adc_raw_vol; // レジスタ更新 // vreg_ctr[ VREG_C_SND_VOL ] = temp; // vreg_twl[ REG_TWL_INT_ADRS_VOL ] = adc_raw_vol / ( 256 / 32 ); // ←adc値でよい // codecに伝える iic_mcu_write_a_byte( IIC_SLA_CODEC, CODEC_REG_VOL, slider_to_codec[ adc_raw_vol / 4 ] ); force_update_vol = 100; } } } // TUNE_LED /////////////////////////////////// // ここで?仕様? { switch ( vreg_ctr[VREG_C_LED_TUNE] ) { case LED_TUNE_ILM_ON: LED_duty_TUNE = vreg_ctr[VREG_C_LED_BRIGHT]; break; case LED_TUNE_ILM_SVR: LED_duty_TUNE = vreg_ctr[VREG_C_TUNE] / 16; break; case LED_TUNE_ILM_OFF: default: LED_duty_TUNE = 0; break; } } adc_updated = 0; } } ADCEN = 1; ADM = 0b00011011; // セレクトモード、章圧、fCLK/6 ///ここから ↓ ADPC = 0x06; // ADCポートのセレクト ADS = ADC_SEL_TUNE; NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); ADCS = 1; // AD開始。 /// ここまで ↑ までに1us=8clk以上開ける ADIF = 0; ADMK = 0; } /* ========================================================  過去3つのminでもMAXでもない値を返す  突発的なノイズを除く。  根本対策ではないが、これはこれで使い道がある。 ======================================================== */ static u8 getmean3( u8 * hist ) { if( *hist > *( hist + 1 ) ) { if( *hist > *( hist + 2 ) ) { return( ( *( hist + 1 ) > *( hist + 2 ) ) ? *( hist + 1 ) : *( hist + 2 ) ); } else { return( *hist ); } }else{ if( *hist > *( hist + 2 ) ) { return( *hist ); } else { return( ( *( hist + 1 ) < *( hist + 2 ) ) ? *( hist + 1 ) : *( hist + 2 ) ); } } } /* ========================================================  自前で次のチャンネル   一通り終わったら止める ======================================================== */ __interrupt void int_adc( ) { static u8 hist_tune[3]; static u8 hist_snd_vol[3]; static u8 hist_bt_temp[3]; static u8 index; EI( ); switch ( ADS ) { /* case ( ADC_SEL_AMB_BRIT ): vreg_ctr[ VREG_C_AMBIENT_BRIGHTNESS ] = ADCRH; break; */ case ( ADC_SEL_TUNE ): hist_tune[index] = ADCRH; #ifdef _MODEL_WM0_ adc_raw_dep = 255 - getmean3( hist_tune ); #else adc_raw_dep = getmean3( hist_tune ); #endif break; case ( ADC_SEL_VOL ): hist_snd_vol[index] = ADCRH; adc_raw_vol = getmean3( hist_snd_vol ); // TWL用レジスタ(32段)の更新。アトミックな処理として扱わないと不都合が。 /// 割り込みはHorizonを通してコマンドを発行されるのを待てばよい break; case ( ADC_SEL_BATT_TEMP ): hist_bt_temp[index] = ADCRH; raw_adc_temperature = getmean3( hist_bt_temp ); renge_task_immed_add( PM_bt_temp_update ); break; case ( ADC_SEL_BATT_DET ): // vreg_ctr[ VREG_C_DBG_BATT_DET ] = ADCRH; // todo break; } // もっとまともな書き方がありそうだ // if( ADS == ADC_SEL_BATT_DET ){ if( ADS != ADC_SEL_BATT_TEMP ) { // 電池判別は電源投入の一回のみ ADS += 1; // 次のチャンネル BT_TEMP_P = 1; // 電池温度監視スタート } else { ADCEN = 0; // 止めてしまう BT_TEMP_P = 0; // 電池温度監視スタート adc_updated = 1; index = ( index == 2 ) ? 0 : ( index + 1 ); } } /* ======================================================== tsk_adcと競合することを考慮していません。 ======================================================== */ u8 get_adc( u8 ch ) { u8 temp; ADMK = 1; ADIF = 0; ADCEN = 1; ADCS = 0; ADM = 0b00100011; // セレクトモード、昇圧、fCLK/6 ///ここから↓ ADPC = 0x06; // ADCポートのセレクト ADS = ch; NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); ADCS = 1; // AD開始。 /// ここまで↑ に、1us以上開ける ADMK = 0; while( ADIF == 0 ){;} temp = ADCRH; ADCEN = 0; return ( temp ); }