/* ======================================================== 藤田@開技 nintendo '09 Apr ======================================================== */ #include "incs.h" #include "adc.h" #include "pm.h" #include "led.h" //#define _4db_ //#define _15db_ #define _10db_ #include "voltable.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; 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_TUNE ] }; u8 vol_data_ctr; u8 vol_data_ctr_tmp; filter_work work_vr_vol = { &vol_data_ctr_tmp }; // ===================================================== // extern void nop8(); static void adc_filter( u8 new_val, filter_work* work ); static u8 adc_scaling( u8 ); // ===================================================== // #define INTERVAL_TSK_ADC 15 /* ======================================================== ADC設定と、開始 以下のピンは主にここで操作・監視されます。 ・BT_TEMP,_P ・ADIN1 ・VOL 関係ありそうですが別のところで管理しています ・PM_BT_DET,_P BT_chk ・8tics毎に呼ばれ、3チャンネル分取り込むとADCを停止します。  タスク起動時、レジスタには前回の取り込み値が入っています。 ======================================================== */ void tsk_adc( ) { static u8 old_tune; static u8 sndvol_codec; static u8 bt_temp_old; /* これやると、Volスライダの反応が劇悪になるので注意 static u8 task_interval = 0; if( task_interval-- != 0 ) { return; } else { task_interval = (u8)( INTERVAL_TSK_ADC / SYS_INTERVAL_TICK ); } */ if( adc_updated ) { adc_updated = false; if( system_status.pwr_state == ON ) { // 3D ///////////////////////////////////////// vreg_ctr[ VREG_C_TUNE ] = 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; renge_task_immed_add( tski_vol_update ); // 2ms後でいいでしょう... } } // バッテリ識別 /////////////////////////// /* 呼ばれません */ } } ADCEN = 1; ADM = 0b00001011; // セレクトモード、昇圧、fCLK/6 ///ここから ↓ ADPC = 0x06; // ADCポートのセレクト ADS = ADC_SEL_TUNE; 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_slider, sent_index_twl; static u8 sent_data; static bit last_modifyer_is_twl; // 0 = ctr if( !( system_status.pwr_state == ON ) || ( system_status.pwr_state == SLEEP )){ return( ERR_FINISED ); } // どの音量にするの? // if( vreg_ctr[ VREG_C_VOL_OPTION ] & ( REG_BIT_VOL_UPDATE_TO_SLIDER ) ) { // 最優先 スライダに上書き sent_index = vol_data_ctr; vreg_ctr[ VREG_C_VOL_OPTION ] &= ~REG_BIT_VOL_UPDATE_TO_SLIDER; } else if( vol_changed_by_ctr ) { // スライダ vol_changed_by_ctr = false; last_modifyer_is_twl = false; if( vreg_ctr[ VREG_C_VOL_OPTION ] & REG_BIT_VOL_FORCE_REG ) { // レジスタから強制セット sent_index_slider = vreg_ctr[ VREG_C_VOL_DIGITAL ]; } else { sent_index_slider = vol_data_ctr; // CTRスライダ } sent_index = sent_index_slider; } 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; set_irq( VREG_C_IRQ2, REG_BIT_TWL_SNDVOL_CHANGE ); } else { // force_sliderを0にしたとき & 書きまくるとき // スライダかTWLの最後にセットした方をセット if( !last_modifyer_is_twl ) { sent_index = sent_index_slider; } else { sent_index = sent_index_twl; } } // レジスタの更新 // vreg_ctr[ VREG_C_SND_VOL ] = sent_index; if( sent_index == 1 ) { vreg_twl[ REG_TWL_INT_ADRS_VOL ] = 1; } else { vreg_twl[ REG_TWL_INT_ADRS_VOL ] = sent_index/2 ; } // CODECに書きに行く? if( vreg_ctr[ VREG_C_VOL_OPTION ] & REG_BIT_VOL_BY_SPI ) { return( ERR_FINISED ); // by SPI ならここまででおしまい // } // 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 ); } /* ========================================================  過去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; volatile u8 adc_data; adc_data = ADCRH; switch ( ADS ) { /* case ( ADC_SEL_AMB_BRIT ): // 環境明るさ vreg_ctr[ VREG_C_AMBIENT_BRIGHTNESS ] = adc_data; break; */ case ( ADC_SEL_TUNE ): EI(); #ifdef _MODEL_WM0_ adc_raw_dep = 255 - adc_data; #else adc_raw_dep = adc_data; #endif break; case ( ADC_SEL_VOL ): EI(); #ifdef _MODEL_CTR_ if( system_status.model == MODEL_TS_BOARD ) { adc_raw_vol = adc_data; } else { adc_raw_vol = 255 - adc_data; } #else adc_raw_vol = adc_data; #endif break; case ( ADC_SEL_BATT_TEMP ): EI(); raw_adc_temperature = adc_data; #ifdef _DEBUG_BT_TEMP_ if( vreg_ctr[ VREG_C_COMMAND3 ] == 't' ) { raw_adc_temperature = vreg_ctr[ VREG_C_DBG01 ]; } #endif if( (( vreg_ctr[ VREG_C_STATUS_1 ] & REG_BIT_GASGAUGE_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; index = ( index == 2 ) ? 0 : ( index + 1 ); // ノイズ取りの配列インデックス } ADIF = 0; // ←これをしないと、いっこ前のチャンネルのデータの完了で直後に割り込む可能性がある } /* ======================================================== tsk_adcと競合することを考慮していません。 ======================================================== */ u8 get_adc( u8 ch ) { u8 temp; ADMK = 1; ADIF = 0; ADCEN = 1; ADM = 0b00001011; // セレクトモード、昇圧、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 ) { u8 temp; if( abs( new_val - *( work -> value_used )) > 2 ) { // 大きく離れた work -> large_diff_count += 1; 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 += 1; } else if( *( work -> value_used ) > new_val ) { work -> diffs -= 1; } 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; } } } #if 0 // getmean使用 // __interrupt void int_adc( ) { static u8 hist_tune[3]; static u8 hist_snd_vol[3]; static u8 hist_bt_temp[3]; static u8 index; volatile u8 adc_data; adc_data = ADCRH; switch ( ADS ) { /* case ( ADC_SEL_AMB_BRIT ): // 環境明るさ vreg_ctr[ VREG_C_AMBIENT_BRIGHTNESS ] = adc_data; break; */ case ( ADC_SEL_TUNE ): hist_tune[index] = adc_data; EI(); #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] = adc_data; EI(); #ifdef _MODEL_CTR_ if( system_status.model == MODEL_TS_BOARD ) { adc_raw_vol = getmean3( hist_snd_vol ); } else { adc_raw_vol = ( 255 - getmean3( hist_snd_vol )); } #else adc_raw_vol = getmean3( hist_snd_vol ); #endif break; case ( ADC_SEL_BATT_TEMP ): hist_bt_temp[index] = adc_data; EI(); raw_adc_temperature = getmean3( hist_bt_temp ); if( (( vreg_ctr[ VREG_C_STATUS_1 ] & REG_BIT_GASGAUGE_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; // 次のチャンネル ADIF = 0; // ←これをしないと、いっこ前のチャンネルのデータの完了で直後に割り込む可能性がある } else { ADCEN = 0; // 止めてしまう adc_updated = true; index = ( index == 2 ) ? 0 : ( index + 1 ); // ノイズ取りの配列インデックス } } #endif