/* ======================================================== 藤田@開技 nintendo '09 Apr ======================================================== */ #include "incs.h" #include "adc.h" #include "pm.h" #include "led.h" #include "vreg_twl.h" // ===================================================== // bit adc_updated; bit vol_changed_by_ctr; bit vol_changed_by_twl; u8 vol_old; u8 adc_raw_vol; u8 adc_raw_dep; u8 vol_polling; u8 vol_level_twl; extern const u8 slider_to_codec[]; // ===================================================== // typedef struct filter_work { u8* value_used; s8 diffs; // KIKAN中の偏り具合 s8 kikan; u8 large_diff_count; }filter_work; filter_work work_vr_3d = { &vreg_ctr[ VREG_C_3D ] }; u8 vol_data_ctr; u8 vol_data_ctr_tmp; filter_work work_vr_vol = { &vol_data_ctr_tmp }; // twl の8段階volのリニア値からの境界 /* twl内の32 -> 8 テーブル 0〜1,〜4,〜8,〜13,〜18,〜23,〜28,31 */ static const u8 TWL_VOL_BOUNDARY[] = { 1, 4, 8, 13, 18, 23, 28, 31 }; // ===================================================== // extern void nop8(); static void adc_filter( u8 new_val, filter_work* work ); static u8 adc_scaling( u8 ); static void update_twl_vol( u8 sent_index ); // ===================================================== // #define INTERVAL_TSK_ADC 15 /* ======================================================== ADC設定と、開始 以下のピンは主にここで操作・監視されます。 ・BT_TEMP,_P ・ADIN1 ・VOL 関係ありそうですが別のところで管理しています ・PM_BT_DET,_P BT_chk ======================================================== */ void tsk_adc( ) { if( adc_updated ) { adc_updated = false; // 3D ///////////////////////////////////////// vreg_ctr[ VREG_C_3D ] = adc_raw_dep; // 生値 // Volume ///////////////////////////////////// { vreg_ctr[ VREG_C_VOL_ADC_RAW ] = adc_raw_vol; adc_filter( adc_scaling( adc_raw_vol ), &work_vr_vol ); // 結果は*work_vr_volから指されるvol_data_ctr 読みにくい... vol_data_ctr = vol_data_ctr_tmp / 4; if( vol_old != vol_data_ctr ) { vol_changed_by_ctr = true; vol_old = vol_data_ctr; vol_polling = 3; // renge_task_immed_add( tski_vol_update ); ↓で登録 } } // バッテリ識別 /////////////////////////// /* 呼ばれません */ } // 書き忘れがあるといやなのでポーリング orz if( vol_polling < 5 ) { renge_task_immed_add( tski_vol_update ); vol_polling = (u8)(200 / SYS_INTERVAL_TICK) + 5; // 5回/sec } vol_polling --; ADCEN = 1; ADM = bits8(0,0,0,0, 1,0,1,1); // セレクトモード、昇圧、fCLK/6 // ここから ↓ ADPC = 0x06; // ADCポートのセレクト ADS = ADC_SEL_3D; nop8(); ADCS = 1; // AD開始。 // ここまで ↑ までに1us=8clk以上開ける ADIF = 0; ADMK = 0; } void vol_reset() { vol_old = vol_data_ctr; vreg_ctr[ VREG_C_SND_VOL ] = vol_data_ctr; // 64段 } /* ========================================================  Volを更新します。  こんな時に登録されます。   ・ユーザーがVolスライダを動かした   ・Horizonに強制更新を指示された (codecリセット時)   ・TWLアプリがVolをいじった ======================================================== */ task_status_immed tski_vol_update() { static u8 sent_index, sent_index_twl; static bit last_modifyer_is_twl; // false = ctr if( !( system_status.pwr_state == ON ) || ( system_status.pwr_state == SLEEP )){ return( ERR_FINISED ); } // どの音量にするの? // if( vol_changed_by_ctr ) { // スライダ vol_changed_by_ctr = false; last_modifyer_is_twl = false; sent_index = vol_data_ctr; } else if( vol_changed_by_twl ) { // TWLアプリ vol_changed_by_twl = false; last_modifyer_is_twl = true; if( vreg_twl[ REG_TWL_INT_ADRS_VOL ] == 0 ) { sent_index_twl = 0; } else { sent_index_twl = vreg_twl[ REG_TWL_INT_ADRS_VOL ] *2 +1; } sent_index = sent_index_twl; } else { // force_sliderを0にしたとき & 書きまくるとき // スライダかTWLの最後にセットした方をセット if( last_modifyer_is_twl ) { sent_index = sent_index_twl; } else { sent_index = vol_data_ctr; } } // レジスタの更新 // vreg_ctr[ VREG_C_SND_VOL ] = sent_index; // twl側更新 update_twl_vol( sent_index ); // codecに伝える /// 同値でも書く iic_mcu_write_a_byte_codec( CODEC_REG_VOL, slider_to_codec[ sent_index ] ); // set_irq( VREG_C_IRQ0, REG_BIT_VR_SNDVOL_CHANGE ); // 割り込み廃止 return( ERR_FINISED ); } static void update_twl_vol( u8 sent_index ) { // スケーリング if( sent_index == 0 ) { vreg_twl[ REG_TWL_INT_ADRS_VOL ] = 0; } else if( sent_index <= 4 ) { vreg_twl[ REG_TWL_INT_ADRS_VOL ] = 2; // 1はミッシングで正解 } else { vreg_twl[ REG_TWL_INT_ADRS_VOL ] = sent_index/2 ; } // 8段階のレベル化。 割り込みを入れるのに必要 { static u8 vol_twl_old; if( vol_twl_old != vreg_twl[ REG_TWL_INT_ADRS_VOL ] ) { // 8段レベルに変換 u8 new_level = 31; u8 i; vol_twl_old = vreg_twl[ REG_TWL_INT_ADRS_VOL ]; for( i=0; i<=7; i++ ) { if( vreg_twl[ REG_TWL_INT_ADRS_VOL ] <= TWL_VOL_BOUNDARY[ i ] ) { new_level = i; break; } } vol_level_twl = new_level; } } } /* ========================================================  自前で次のチャンネル   一通り終わったら止める ======================================================== */ __interrupt void int_adc( ) { volatile u8 adc_data = ADCRH; switch ( ADS ) { /* case ( ADC_SEL_AMB_BRIT ): // 環境明るさ vreg_ctr[ VREG_C_AMBIENT_BRIGHTNESS ] = adc_data; break; */ case ( ADC_SEL_3D ): EI(); adc_raw_dep = adc_data; break; case ( ADC_SEL_VOL ): EI(); if( system_status.model == MODEL_TS_BOARD ) { adc_raw_vol = adc_data; } else { adc_raw_vol = 255 - adc_data; } break; case ( ADC_SEL_BATT_TEMP ): EI(); if( vreg_ctr[ VREG_C_HAL_OVW_TEMPERATURE ] == 0xFF ) { raw_adc_temperature = adc_data; } else { raw_adc_temperature = vreg_ctr[ VREG_C_HAL_OVW_TEMPERATURE ]; } if(// (( vreg_ctr[ VREG_C_STATUS_1 ] & REG_BIT_MGIC_ERR ) == 0 ) && (( system_status.pwr_state == ON ) || ( system_status.pwr_state == SLEEP ) ) ) { renge_task_immed_add( tski_BT_temp_update ); } break; /* 呼ばれません case ( ADC_SEL_BATT_DET ): break; */ } // もっとまともな書き方がありそうだ if( ADS < ADC_SEL_BATT_DET ) { ADS += 1; // 次のチャンネル } else { ADCEN = 0; // 止めてしまう adc_updated = true; } ADIF = 0; // ←これをしないと、いっこ前のチャンネルのデータの完了で直後に割り込む可能性がある } /* ======================================================== tsk_adcと競合することを考慮していません。 ======================================================== */ u8 get_adc( u8 ch ) { u8 temp; ADMK = 1; ADIF = 0; ADCEN = 1; ADM = bits8(0,0,0,0, 1,0,1,1); // セレクトモード、昇圧、fCLK/6 ///ここから↓ ADPC = 0x06; // ADCポートのセレクト ADS = ch; nop8(); ADCS = 1; // AD開始。 /// ここまで↑ に、1us以上開ける ADIF = 0; while( ADIF == 0 ){;} temp = ADCRH; ADCEN = 0; ADMK = 0; return ( temp ); } /* ======================================================== VRの可動範囲を考えてスケーリング 音量Vol専用 使い回すならそのときどうにかする ======================================================== */ static u8 adc_scaling( u8 orig_val ) { u16 temp; if( orig_val <= vreg_ctr[ VREG_C_VOL_CAL_MIN ] ) { return( 0 ); } if( orig_val >= vreg_ctr[ VREG_C_VOL_CAL_MAX ] ) { return( 255 ); } temp = (u16)(( orig_val - vreg_ctr[ VREG_C_VOL_CAL_MIN ] ) * 256 ) / ( vreg_ctr[ VREG_C_VOL_CAL_MAX ] - vreg_ctr[ VREG_C_VOL_CAL_MIN ] ); if( temp > 255 ) { temp = 255; } return( (u8)( temp & 0xFF ) ); } /* ======================================================== 似非ヒステリシス V2 四捨五入的な動きします ======================================================== */ #define KIKAN 16 static void adc_filter( u8 new_val, filter_work *work ) { if( abs( new_val - *( work -> value_used )) > 2 ) { // 大きく離れた work -> large_diff_count ++; if( work -> large_diff_count > 16 ) { *( work -> value_used ) = new_val; work -> diffs = 0; work -> kikan = KIKAN; } } else { work -> large_diff_count = 0; // 近所の値でも、ある期間でいっぱい偏っていたらそっちに寄せる if( *( work -> value_used ) < new_val ) { work -> diffs ++; } else if( *( work -> value_used ) > new_val ) { work -> diffs --; } if( --( work -> kikan ) == 0 ) { if( ( work -> diffs ) == KIKAN ) // if( ( work -> diffs ) > (s8)( KIKAN * 0.8 ) ) { *( work -> value_used ) = *( work -> value_used ) + 1; } else if( ( work -> diffs ) == ( -1 * KIKAN ) ) // else if( ( work -> diffs ) < (s8)( -1 * KIKAN * 0.8 ) ) { *( work -> value_used ) = *( work -> value_used ) - 1; } work -> diffs = 0; work -> kikan = KIKAN; } } }