#pragma interrupt INTAD callbk_int_ad /****************************************************************************** 1Ch ADCリード de JHL 藤田@開技 '07 Dec *******************************************************************************/ #include "incs.h" extern bit adc_calib_mode; extern u8 vregs[]; extern bit pwon_wt_sel; // Vref = 2.8V // F B 7 3 1 0 u8 const threshold_Co[] = { 99, 82, 67, 31, 0 }; // batt == 0 u8 const threshold_Co_NAND[] = { 99, 82, 68, 46, 27 }; u8 const threshold_PSS[] = { 77, 52, 33, 11, 0 }; // batt == 5 u8 const threshold_PSS_NAND[] = { 82, 55, 42, 30, 27 }; u8 const threshold_IS[] = { 78, 62, 48, 32, 0 }; // batt == 3, vref = 3.3v、オフセットあり(1.8V) u8 const threshold_IS_NAND[] = { 78, 62, 48, 32, 16 }; // 非 NAND アプリは 0 になりません。 /* >> (AVref=3.3Vとした,AOUTの電圧) >>  100% 1.95 〜 5 F >>   75% 1.9 〜 1.95V 4 B >>   50% 1.85 〜 1.9V 3 7 >>   25% 1.8 〜 1.85V 2 3 >>   5% 1.75 〜 1.8V 1 1 >>   2% 1.7 〜 1.75V 0 0 */ u16 vBatt_raw; // 補正値加算前 u16 vBatt; // 移動平均をとった物 u8 adc_comp_val; // 補正値。閾値テーブルを作るときに加算 u16 Vbatt_threshold[5]; u8 battLvl; u8 battLvl_old; // 割り込みをかけるため。使い回してます /****************************************************************************** 電池電圧→電池残量換算 電池劣化とか、負荷による電池電圧の降下は気にしません。 BOOTモードのときは来ません 25Hz( = 40ms間隔 )で呼ばれます ******************************************************************************/ void tsk_adc(){ static u16 vBatt_vals[ 9 ]; static u8 trigger_counter; static u16 geta; static u16 vBatt_last_wake; static u8 battLvl_hist; static u8 battLvl_count; u8 battLvl_now; static u8 vBatt_th_hys[ 5 ]; static u8 on_start = 0; #ifdef debug_codes if( !pwon_wt_sel ){ #endif battLvl_old = battLvl; // パルス性のノイズを取り、データの取り込み vBatt_vals[ 8 ] = vBatt_vals[ 7 ]; vBatt_vals[ 7 ] = vBatt_vals[ 6 ]; vBatt_vals[ 6 ] = vBatt_vals[ 5 ]; vBatt_vals[ 5 ] = vBatt_vals[ 4 ]; vBatt_vals[ 4 ] = vBatt_vals[ 3 ]; vBatt_vals[ 3 ] = vBatt_vals[ 2 ]; vBatt_vals[ 2 ] = vBatt_vals[ 1 ]; if( ( ( vBatt_vals[ 0 ] << 1 ) < ( vBatt_vals[ 1 ] + vBatt_raw + 5 ) ) && ( ( vBatt_vals[ 0 ] << 1 ) > ( vBatt_vals[ 1 ] + vBatt_raw - 5 ) ) ){ vBatt_vals[ 1 ] = vBatt_vals[ 0 ]; }else{ vBatt_vals[ 1 ] = ( ( vBatt_vals[ 0 ] + vBatt_raw ) >> 1 ); } vBatt_vals[ 0 ] = vBatt_raw; vBatt = ( ( vBatt_vals[ 8 ] + vBatt_vals[ 7 ] + vBatt_vals[ 6 ] + vBatt_vals[ 5 ] + vBatt_vals[ 4 ] + vBatt_vals[ 3 ] + vBatt_vals[ 2 ] + vBatt_vals[ 1 ] ) >> 3 ); // スリープ時の、ゲタ見積もり if( PM_SLP == 0 ){ trigger_counter = 0; geta = 0; }else{ trigger_counter++; if( trigger_counter == 1 ){ ; // カウンタの帳尻合わせです }else if( trigger_counter == 2 ){ vBatt_last_wake = vBatt_vals[ 8 ]; if( vBatt_last_wake > vBatt_vals[ 7 ] ) vBatt_last_wake = vBatt_vals[ 7 ]; if( vBatt_last_wake > vBatt_vals[ 6 ] ) vBatt_last_wake = vBatt_vals[ 6 ]; if( vBatt_last_wake > vBatt_vals[ 5 ] ) vBatt_last_wake = vBatt_vals[ 5 ]; if( vBatt_last_wake > vBatt_vals[ 4 ] ) vBatt_last_wake = vBatt_vals[ 4 ]; if( vBatt_last_wake > vBatt_vals[ 3 ] ) vBatt_last_wake = vBatt_vals[ 3 ]; if( vBatt_last_wake > vBatt_vals[ 2 ] ) vBatt_last_wake = vBatt_vals[ 2 ]; if( vBatt_last_wake > vBatt_vals[ 1 ] ) vBatt_last_wake = vBatt_vals[ 1 ]; }else if( trigger_counter < 127 ){ geta = vBatt - vBatt_last_wake; if( geta > 0x8000 ){ geta = 0; } }else{ trigger_counter--; } } if( ( vBatt - geta ) > Vbatt_threshold[ 0 ] ){ battLvl_now = 0x05; }else if( ( vBatt - geta ) > Vbatt_threshold[ 1 ] ){ battLvl_now = 0x04; }else if( ( vBatt - geta ) > Vbatt_threshold[ 2 ] ){ battLvl_now = 0x03; }else if( ( vBatt - geta ) > Vbatt_threshold[ 3 ] ){ battLvl_now = 0x02; }else if( ( vBatt - geta ) > Vbatt_threshold[ 4 ] ){ battLvl_now = 0x01; }else{ battLvl_now = 0x00; } DBG_IIC_REG_TMP3( (u8)vBatt ); DBG_IIC_REG_TMP2( battLvl_now ); if( battLvl_hist != battLvl_now ){ battLvl_count = 0; battLvl_hist = battLvl_now; }else{ ++battLvl_count; if( on_start < 30 ){ // 起動直後は電池電圧早く出す on_start++; if( battLvl_count > 10 ){ // 何回か同じ値を連続でとったら、更新 battLvl_count = 0; battLvl = battLvl_now; } }else{ // 通常運転 if( battLvl_count > 25 ){ // 何回か同じ値を連続でとったら、更新 battLvl_count = 0; if( n_ac_supp == 0 ){ // アダプタの有無で一方通行 // アダプタの有り if( battLvl_now > battLvl ){ battLvl++; } if( ( battLvl_now != battLvl ) && ( battLvl_now == 0x00 ) ) { // タイミングが悪く、アダプタありにも関わらずbatt-empty battLvl = 0; } }else{ // アダプタなし if( battLvl_now < battLvl ){ battLvl--; } } } } } #ifdef debug_codes } #endif switch( battLvl ){ // マイコン遅い case( 2 ): vregs[ REG_INT_ADRS_POWER_INFO ] = 0x03; break; case( 3 ): vregs[ REG_INT_ADRS_POWER_INFO ] = 0x07; break; case( 4 ): vregs[ REG_INT_ADRS_POWER_INFO ] = 0x0B; break; case( 5 ): vregs[ REG_INT_ADRS_POWER_INFO ] = 0x0F; break; case( 0 ): vregs[ REG_INT_ADRS_POWER_INFO ] = 0x00; break; default: vregs[ REG_INT_ADRS_POWER_INFO ] = 0x01; break; } #ifdef debug_saito_special GPIO_dir = 0; GPIO_dat = (bit)( battLvl & 0x01 ); #endif chk_obs_pmic_batt_low(); // NTR向け、電池残量減少通知 #ifdef debug_codes if( !pwon_wt_sel ){ chk_irq0_batt(); // 電池残量減少割り込み } #else chk_irq0_batt(); #endif ADC_Start(); } /****************************************************************************** NTRの一部のソフトが、PMIC側の battery_low ビットを見ているのでセットしてやる ******************************************************************************/ void chk_obs_pmic_batt_low(){ // 減ってきたので通知 if( ( 0x02 < battLvl_old ) && ( battLvl <= 0x02 ) ){ iic2m_write_data( IIC_SLV_ADDR_PMIC, 0x02, 0x10 ); // LEDを赤相当に } // 増えてきたので復帰 if( ( battLvl_old <= 0x03 ) && ( 0x03 < battLvl ) ){ iic2m_write_data( IIC_SLV_ADDR_PMIC, 0x02, 0x00 ); // LEDを緑相当に } } /****************************************************************************** 電池減ってきた割り込みをかける ******************************************************************************/ void chk_irq0_batt(){ static u8 irq_hist = 0; // low割り込み if( ( battLvl == 0x01 ) && ( irq_hist == 0 ) ){ irq_hist = 0x02; vregs[ REG_INT_ADRS_IRQ ] |= 0x20; if( is_TWL ){ // DI(); n_irq_ast; n_irq_ngt; // EI(); } } if( with_NAND ){ // empty割り込み // 電圧検出の所の仕様により、残量1を飛び越して0になることはない...はず。 if( battLvl == 0x00 ){ if( irq_hist == 0x00 ){ // 突然 emptyになった? irq_hist = 0x03; vregs[ REG_INT_ADRS_IRQ ] |= 0x30; // そうなら、両方立てる if( is_TWL ){ // DI(); n_irq_ast; n_irq_ngt; // EI(); } }else if( irq_hist == 0x02 ){ // 以前にLow割り込みをかけた irq_hist = 0x03; vregs[ REG_INT_ADRS_IRQ ] |= 0x10; // 割り込みを掛けまくらないように if( is_TWL ){ // DI(); n_irq_ast; n_irq_ngt; // EI(); } } } } // 電池あがってきたらフラグ消してしまう if( battLvl >= 0x01 ){ // low以上なら // DI(); vregs[ REG_INT_ADRS_IRQ ] &= ~0x10; // emptyなかったことに // EI(); irq_hist &= ~0x01; } if( battLvl >= 0x02 ){ // lowより上なら // DI(); vregs[ REG_INT_ADRS_IRQ ] &= ~0x30; // lowもemptyもなかったことに // EI(); irq_hist &= ~0x03; } } /****************************************************************************** 電池が判明/モード切替で閾値セット ******************************************************************************/ void set_batt_th(){ u8 *p_tbl; u8 tmp; if( with_NAND ){ if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_CO ){ // Co (0b000) p_tbl = &threshold_Co_NAND[0]; }else if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_IS ){ // IS (0b011) p_tbl = &threshold_IS_NAND[0]; }else{ // PSS (0b111) p_tbl = &threshold_PSS_NAND[0]; } }else{ // NAND 保護なし if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_CO ){ p_tbl = &threshold_Co[0]; }else if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_IS ){ p_tbl = &threshold_IS[0]; IS_led_cam = 0; // 赤箱対応 IS_led_cam_mode = 0; // 赤箱対応 }else{ p_tbl = &threshold_PSS[0]; } } for( tmp = 0; tmp != 5; tmp++ ){ Vbatt_threshold[ tmp ] = 512 + adc_comp_val + *p_tbl; p_tbl++; } } /****************************************************************************** VoffLの測定 4ms間隔で取り込み、必要なところの8この平均取ります。 ____________________________ /RESET ______ ---------_________|←30ms →| VDET+ →+-----____ | \____ ADCサンプリング ***||||||||****  この辺 8個の平均とる ******************************************************************************/ void tsk_adc_calib(){ u8 adc_avg; static u8 count_for_IS = 0; static u8 state = 0; static u8 adc_raw_data_B[8], adc_raw_data_A[4]; // 平均対象のB,対象外のA static u16 adc_sum8 = 0; static u8 adc_rec_val = 0; static u8 count = 0; switch( state ){ case( 0 ): // 下準備 IIC0_Stop(); TMH0_Stop(); // pow_R TMH1_Stop(); // pow_B TM50_Stop(); // wifi P1.7 = 1; // Y P1.5 = 1; // R P1.6 = 1; // B CR51 = 125 - 1 ; // T = 4ms iic2m_write_data( IIC_SLV_ADDR_PMIC, 0x02, (u8*)0x00 ); state = 1; break; case( 1 ): // スリープ待ち。赤箱なら待たない // if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_IS ){ // state = 2; // }else{ { if( PM_SLP == 1 ){ P1.7 = 0; // Y vregs[ REG_INT_ADRS_POWER_SAVE ] = 0x07; PM_set_VRSET(); state = 2; } } break; case( 2 ): // vBatt_raw は補正されてない 素 の値です // 値域がだいたい決まっているので、下8ビットしかみない。 // 余裕をみて 3.1〜3.5Vでみても、567〜640なので、512引いて、55〜128でまだまだ余裕 // それ以上だったら取得失敗扱い(無視) if( ( vBatt_raw > ( 512 + 0x60 ) ) || ( vBatt_raw < ( 512 + 0x40 ) ) ){ break; } // まず、計算対象のバッファ8個を埋める adc_raw_data_B[ count ] = (u8)( vBatt_raw & 0x00FF ); // マスクしない。信じてる。 adc_sum8 += ( vBatt_raw & 0x00FF ); if( count == 7 ){ // 8回ためる count = 0; state = 3; }else{ count++; } break; case( 3 ): // 次に、ディレイ用のバッファ4個を埋める adc_raw_data_A[ count ] = (u8)( vBatt_raw & 0x00FF ); if( count == 3 ){ // 4回ためる P1.6 = 0; // B count = 0; state = 4; }else{ count++; } break; default: if( n_sys_reset != 0 ){ // リセットを見るのはどちらかというと誤書き込み防止 // 微妙タイミングなら、放電の方が早くてたぶん書き込み完了しない adc_avg = (u8)( adc_sum8 / 8 ); // なんと、半端なシフトは割り算になる(25clk)シフト&調整より早いと言うことか。 // IS対応 if( vregs[ REG_INT_ADRS_BATT_INFO ] == BATT_IS ){ adc_avg -= ( 9 + 16 ); // キャリブレーションが1.8V @ ref = 3.3 を1.65Vに換算 if( flash_write( DAT_ADC_COMP, &adc_avg ) == 0 ){ // 目立たないけど、ここで書き込み mcu_reset; } } // 必要なら記録 if( adc_rec_val != adc_avg ){ if( flash_write( DAT_ADC_COMP, &adc_avg ) == 0 ){ P1.5 = 0; // R P1.6 = 1; // B adc_rec_val = adc_avg; P1.6 = 0; // B P1.5 = 1; // R } } // リングバッファ等更新 if( ( vBatt_raw & 0x00FF ) < 128 ){ adc_sum8 -= adc_raw_data_B[ count ]; // 合計から古いのを引く adc_raw_data_B[ count ] = adc_raw_data_A[ ( count & 0x03 ) ]; // プリバッファから補充 adc_sum8 += adc_raw_data_B[ count ]; // 合計に 新しいのを足す adc_raw_data_A[ ( count & 0x03 ) ] = (u8)( vBatt_raw & 0x00FF ); // プリバッファを更新 // あんまりうれしくないかも.. count = ( count < 7 )? count + 1: 0; // 上手な書き方募集 } } } ADC_Start(); } //**************************************************************************** __interrupt void callbk_int_ad(){ EI(); vBatt_raw = ( ADCR >> 6 ); // 右につめておきます...なんで。 ADC_Stop(); } //**************************************************************************** void ADC_Init(){ ADPC = PORT_ADPC_3DIO; // /IRQ_0 は親のほうがプルアップしてる ADM = 0x90; // fAD = fPRS/6, wait:2〜13ClkSys ADS = 0x03; } void ADC_Start(){ ADM.0 = 1; // ADCE NOP(); // 1usあけなきゃいけないっす NOP(); // nop : 2clkです。 NOP(); NOP(); ADM.7 = 1; // ADCS MK1L.0 = 0; // ADC割り込みマスク } void ADC_Stop(){ MK1L.0 = 1; // ADMK = 1; ADM.7 = 0; // ADCS ADM.0 = 0; // ADCE }