/* ======================================================== task_sys 電源周りを司るのタスク $Id$ ======================================================== */ #ifndef _WIN32 #pragma SFR #pragma NOP #pragma HALT #pragma STOP #endif #include "incs.h" #include "i2c_twl.h" #include "i2c_ctr.h" #include "led.h" #include "accero.h" #include "pm.h" #include "rtc.h" #include "sw.h" #include "adc.h" #include "self_flash.h" #include "i2c_mcu.h" #include "vreg_twl.h" //========================================================= #define WAIT_SHIROBAKO_POW_CONTROL 240 //========================================================= extern void nop8(); static void chk_emergencyExit(); static void force_off_check(); static void send_getup_to_soc(); #ifdef _DBG_CHK_OFF_LEAK_ static void leak_check(); #endif //========================================================= extern bit info_led_off; extern bit going_to_sleep; extern bit bt_authorized; static u8 timeout_sleep; extern u8 chg_led_override; #ifdef i2c_timeout_test extern bit i2c_mcu_time_out_error; #endif //========================================================= #define PWSW_POWON_TIME (u8)( 20 / SYS_INTERVAL_TICK ) // [ms] // INTERVAL_TSK_SW で割らない。値が元から小さいので #define PWSW_ON_CHECK_TIMEOUT 100 // 適当 電源onの為に電源スイッチが操作されたとき、この時間経過したらmcuを省電力に戻す /********************************************//** 電源の立ち上げやスリープなどを管理 ***********************************************/ void tsk_sys( ) { static u8 pwsw_timeout = 0; // 電源ボタンチャタリング回避。 タイムアウトするまでに電源投入確定しないとoffに戻る hal_update(); switch ( system_status.pwr_state ) { case ON_CHECK: //------------------------------------------------------- // 強制電源断カウントダウンタイマクリア clear_pow_off_countdown(); // 他の割り込みでHALT(スリープ)が解除 / アダプタ有時 // 電源スイッチでHALT解除 // if( system_status.poweron_reason == RSN_PWSW ) { if( PM_EXTDC_n ) { // アダプタなし if( SW_pow_count != 0 ) { pwsw_timeout = 0; } else { pwsw_timeout ++; } if( pwsw_timeout > PWSW_ON_CHECK_TIMEOUT ) { SW_pow_count = 0; system_status.pwr_state = OFF_TRIG; // スイッチはノイズだった。寝る。 renge_task_interval_run_force = true; return; } } else { BT_chk(); } if( SW_pow_count < PWSW_POWON_TIME ) { // 押し時間が短くて電源onに到達していない return; } } // else { if( system_status.poweron_reason == RSN_TRIAL、… ) 電源投入 } SW_pow_mask = true; // pwsw押しっぱなしで電源入/切 を繰り返さないように // 電源投入 // iic_mcu_start( ); bt_force_update = false; BT_chk(); // 実機やバッテリの判定、電池残量ICの設定 #ifndef _ALLOW_NOBATT_ if( system_status.model == MODEL_JIKKI_NOBATT ) { renge_task_interval_run_force = true; system_status.pwr_state = OFF_TRIG; return; } #endif // 残量チェック BT_get_left(); // 先に、BT_chk()が実行されている必要があります。 if( // ( vreg_ctr[VREG_C_BT_REMAIN] < 1 ) // こっちで判定すると電池がほとんど無いときに // // アダプタ差しても数分起動できなくなっちゃう ( vreg_ctr[VREG_C_BT_VOLTAGE] < ( V_TH_ZERO / 256 ) ) // 電池空っぽ ) { // 電池が少ないので起動させない(電圧チェックもされてる) renge_task_interval_run_force = true; system_status.pwr_state = OFF_TRIG; return; } // ポートの方向設定 電源入れる前に。 PM_SW_WIFI_n = 1; PM_SW_HOME_n_JIKKI = 1; PM_WL_TX = 1; PM_SW_SEL_n = 1; PM_ACCEL_INT1 = 1; PM_ACC_VALID = 1; if( PM_sys_pow_on( ) != ERR_SUCCESS ) // リセット解除もしてきます { // 電源起動不可エラー renge_task_interval_run_force = true; system_status.pwr_state = OFF_TRIG; return; } // ここまで来ると、電源投入確定 // // プルアップon PU_BT_IN_CHG_n = 1; PU7 = bits8(0,0,0,1, 1,1,0,1); // 4:SW_WIFI 3:SW_PWSW 2:PM_IRQ 0:PM_EXTDC_n PU_SW_HOME_n_JIKKI = 1; // SW_HOME if( system_status.poweron_reason == RSN_PWSW || system_status.poweron_reason == RSN_TRIAL ) { // 電源ボタンでのonの時は、LEDを点灯させる vreg_ctr[VREG_C_LED_POW] = LED_POW_ILM_AUTO; } else { // とりあえず、LED消灯状態で起動させる vreg_ctr[VREG_C_LED_POW] = LED_POW_ILM_OFF; } system_status.pwr_state = ON_TRIG; PM_LCD_vcom_set( ); // LCDの対向電圧値など書き込み break; case ON_TRIG: //------------------------------------------------------- #ifdef i2c_timeout_test LED_duty_pow_blu = 0; // debug LED_duty_3d = 0; LED_duty_notify_red = 0; LED_duty_notify_grn = 0; LED_duty_notify_blu = 0; LED_pow_red = 0; LED_CAM = 0; i2c_mcu_time_out_error = false; #endif IIC_ctr_Init( ); IIC_twl_Init( ); RTC_32k_on( ); vreg_twl_init( ); vreg_ctr_reset( ); KRM = bits8(0,0,0,0, 0,0,0,0); PIF0 = 0; system_status.poweron_reason = NONE; renge_task_interval_run_force = true; MK0 = INT_MSK0_RSV; MK1 = INT_MSK1_RSV; iic_mcu_start(); // MK2 = ~( INT_MSK2_IIC_TWL | INT_MSK2_WIFI_TX_BSR | INT_MSK2_CODEC_PMIRQ ); // PMK21 = 0; // wifi 使わない PMK6 = 0; // pm_irq // todo 将来的には完全にレジスタ制御に RBR_RESET_n = 1; RBR_FLIGHT = 0; // リブート時、ステータスを何となく更新 if( system_status.reboot ) { u8 bl_status_temp; bl_status_temp = read_pmic( PM_REG_ADRS_BL ); vreg_ctr[ VREG_C_STATUS ] |= (( bl_status_temp & 0x03 ) << 5 ); set_bit( ( read_pmic( PM_REG_ADRS_VDD_LCD ) != 0 ), vreg_ctr[ VREG_C_STATUS ], REG_BIT_LCD_POW ); BT_chk(); } LED_init( ); // reboot時の↑BT_Chk,BT_chk後に行いたい system_status.reboot = 0; system_status.pwr_state = ON; // WDTリセット時、I2Cの初期化まで割り込み保留 // ほんとはここにべた書きしたくないが... if( ( vreg_ctr[ VREG_C_MCU_STATUS ] & REG_BIT_STATUS_WDT_RESET ) != 0 ) { set_irq( VREG_C_IRQ0, REG_BIT_IRQ_WDT_RESET ); } break; case ON: //--------------------------------------------- // PMICによる強制電源断チェック // デバッガがreset1をアサートすることもある。そのときは全部リセット chk_emergencyExit(); // SLP監視 if( going_to_sleep ) // 絶対に SLP_REQ の前に予告が来る { timeout_sleep ++; if( timeout_sleep == 0 || // オーバーフローを期待。sleepするって言ったけど一瞬で起きて気がつかなかった ( PIF0 && !SLP_REQ )) // slp割り込みが入った気がしたが、もう起きてしまった { PIF0 = 0; send_getup_to_soc(); } if( PIF0 && SLP_REQ ){ PIF0 = 0; PM_VDD_ecoMode(); system_status.pwr_state = SLEEP; renge_task_interval_run_force = true; } } // 強制offカウント force_off_check(); #ifndef _TAIKENDAI_SEISAN_SPECIAL_ if( system_status.taikendai ) // アダプタが抜けたら自動で電源off { if( PM_EXTDC_n ) { system_status.pwr_state = OFF_TRIG; } } #endif break; case SLEEP: //------------------------------------------ chk_emergencyExit(); // スリープから復帰 if( !SLP_REQ ){ PM_VDD_normMode(); wait_ms( 5 ); // tdly_sw send_getup_to_soc(); system_status.pwr_state = ON; } force_off_check(); // leak_check(); #ifndef _TAIKENDAI_SEISAN_SPECIAL_ if( system_status.taikendai ) // アダプタが抜けたら自動で電源off { if( PM_EXTDC_n ) { system_status.pwr_state = OFF_TRIG; } } break; // ←ここでよい。 #else /* FALLTHROUGH */ #endif default: //--------------------------------------- system_status.pwr_state = OFF_TRIG; // あり得ないステート。デバッグ用 // dbg_nop(); /* FALLTHROUGH */ case OFF_TRIG: //--------------------------------------- // LED消灯を待つ vreg_ctr[ VREG_C_LED_POW ] = LED_POW_ILM_OFF; vreg_ctr[ VREG_C_LED_WIFI ] = WIFI_LED_OFF; vreg_ctr[ VREG_C_LED_3D ] = LED_3D_ILM_OFF; info_led_off = true; if( LED_duty_pow_blu != 0 ) { return; } // 強制電源断カウントダウンタイマクリア clear_pow_off_countdown(); // 加速度センサ停止 vreg_ctr[ VREG_C_ACC_CONFIG ] = 0x00; tski_acc_setup(); EI(); // DI状態でかえって来るので、I2C_mcu 完了させるのに必要を // その他ペリフェラル停止 LED_stop( ); IIC_ctr_Stop( ); IIC_twl_Stop( ); RTC_32k_off(); // todo マクロにでもする RBR_RESET_n = 0; RBR_FLIGHT = 0; // 電源オン条件の割り込みセット // PWSW KR3 押すとL // BG24 KR4 // ふた開け INTP5 閉じるとL // ACアダプタ INTP4 アダプタありでL // RTC while( iic_mcu_busy ) {;} // irqマスク設定 KRM = ( KR_SW_POW ); // Mask ではなく、Modeなのだそうだ。紛らわしい MK0 = ~( INT_MSK0_EXTDC | INT_MSK0_WDTI ); MK1 = ~( INT_MSK1_KR | INT_MSK1_RTCINTVAL ); // INT_MSK1_RTCALARM | RTCアラーム封印中 MK2L = 0xFF; // irqフラグクリア vreg_ctr[VREG_C_IRQ0] = 0; vreg_ctr[VREG_C_IRQ1] = 0; vreg_ctr[VREG_C_IRQ2] = 0; vreg_ctr[VREG_C_IRQ3] = 0; // PU5 そのまま PU7 = bits8(0,0,0,0, 1,0,0,1); // PWSWI,PM_EXTTDC,( IRQ0_deactive(), PM_IRQ_deactive ) PU_SW_HOME_n_JIKKI = 0; // SW_HOME 停止 IF0 = 0; IF1 = 0; IF2 = 0; IRQ0_disable; PM_sys_pow_off( ); // 電源カットオフ // ポート類を停止モードに。こういう回路でも、L出力にしなきゃだめだそうな。 SW_WIFI_n_RAW = 0; PM_SW_WIFI_n = 0; SW_HOME_n_JIKKI_RAW = 0; PM_SW_HOME_n_JIKKI = 0; WL_TX = 0; PM_WL_TX = 0; SW_SEL_n = 0; PM_SW_SEL_n = 0; ACCEL_INT1 = 0; PM_ACCEL_INT1 = 0; ACC_VALID = 0; PM_ACC_VALID = 0; system_status.pwr_state = OFF; SW_pow_mask = true; SW_pow_count = 0; // HALデバッグリセット 忘れると電源on出来なくなることが… hal_reset(); /* FALLTHROUGH */ case OFF: // 注:現状では電源ボタンしか電源投入はありません。 system_status.poweron_reason = NONE; pwsw_timeout = 0; if( !PM_EXTDC_n || chg_led_override != 0 ) // これがゼロになるまで待つ { // アダプタ有り /////////////////////////////// または、アダプタさして3秒は充電LEDを点けている期間 // 電源ボタン押しチェックへ system_status.poweron_reason = RSN_PWSW; system_status.pwr_state = ON_CHECK; // 試遊台なら自動で電源再投入 if( system_status.taikendai ) { vreg_ctr[ VREG_C_HAL_OVW_CONT0 ] = vreg_ctr[ VREG_C_HAL_OVW_CONT1 ] = 0; wait_ms(46); // pmicの電源再投入仕様のため system_status.poweron_reason = RSN_TRIAL; system_status.pwr_state = ON_CHECK; } } else { // アダプタなし /////////////////////////////// // 省電力へ移行 iic_mcu_stop( ); pm_chk_adapter(); PM_Chg_Stop(); bt_force_update = true; while( RWST ) {;} // 割り込み待ちで寝る // RTCIMK = 1; // systick用インターバルタイマー割り込みマスク #ifdef _DBG_CHK_OFF_LEAK_ leak_check(); #endif CKC = bits8(0,0,0,0, 1,0,0,1); // 4MHzに落としてからSTOPしなくてはならない OSMC = bits8(0,0,0,0, 0,0,0,0); // 5MHz以下モードへ。 if( PM_EXTDC_n_RAW ) // きわどいタイミングで挿抜が起きることがある。HAL通さないべき(off中ですし) { STOP(); } // 起きる // // 起きる条件は // ・KeyReturn割り込み(電源ボタン) // ・RTCアラーム(現在封印) // クロック復帰 OSMC = bits8(1,0,0,0, 0,0,0,1); // 8MHz動作準備 renge_flg_interval = 0; hal_update(); CKC = bits8(0,0,0,0, 1,0,0,0); // FSEL = 1 後3clk以上開ける // 起きた理由は? if( !PM_EXTDC_n ){ system_status.pwr_state = OFF; // 一回回ってきて、ON_CHECKに行く } /* // RTCアラーム。封印中 else if( vreg_ctr[ VREG_C_IRQ1 ] & REG_BIT_RTC_ALARM ) { system_status.poweron_reason = RSN_RTC_ALARM; system_status.pwr_state = ON_CHECK; } // else if( 他の割り込みで電源on ){ // 現状他の要因では起きない // } */ else // if( !SW_POW_n ) // (それ以外なら)電源ボタンで起きた { SW_pow_mask = false; system_status.poweron_reason = RSN_PWSW; system_status.pwr_state = ON_CHECK; } RTCIMK = 0; // sys tick タイマー有効 if( system_status.taikendai ) { system_status.pwr_state = OFF_TRIG; } } } } /********************************************//** PMICが電源異常で止めたか確認 ***********************************************/ static void chk_emergencyExit(){ static u8 shirobako_power_control_count; if( shirobako_power_control_count == 0 ) { if( !RESET1_n ) // PM_chk_LDSW() はI2C_mを使用し、高コスト { if( !PM_chk_LDSW() ) { // リセットが下がってる /// PMICが異常終了判断をした system_status.pwr_state = OFF_TRIG; renge_task_interval_run_force = true; } else { // 白箱の仕業 shirobako_power_control_count = 1; } } } else { if( shirobako_power_control_count == WAIT_SHIROBAKO_POW_CONTROL ) // マジックナンバー // デバッガが何かした。reset1を解除するまでは無視 { if( RESET1_n ) // リセットネゲート待ち { shirobako_power_control_count = 0; } else { // nothing to do } } else if( shirobako_power_control_count == 200 ) // * (sys_tick)[ms] // デバッガが何かしたいらしい { #ifndef _RVD_ // 白箱は電源を切りたいらしい system_status.pwr_state = OFF_TRIG; renge_task_interval_run_force = true; shirobako_power_control_count = WAIT_SHIROBAKO_POW_CONTROL; #endif } else { if( !RESET1_n ) { if( shirobako_power_control_count != 255 ) shirobako_power_control_count ++; } else { // (TSボードで手動で/デバッガが)リセットをかけたらしい send_cmd_pmic( PM_REG_ADRS_BL, 0 ); vreg_ctr[VREG_C_STATUS] = ( vreg_ctr[VREG_C_STATUS] & bits8(1,0,0,1, 1,1,1,1) ); vreg_ctr[VREG_C_COMMAND0] |= REG_BIT_RESET1_REQ; renge_task_immed_add( tski_do_command0 ); shirobako_power_control_count = WAIT_SHIROBAKO_POW_CONTROL; } } } } /********************************************//** - 電源ボタン長押し - 電池切れ - 電池抜け での強制OFF。発動すると解除不能 ***********************************************/ static void force_off_check() { if( force_off ) { system_status.pwr_state = OFF_TRIG; renge_task_interval_run_force = true; } } /********************************************//** SoCを起こす   電圧が通常に戻ってから呼んで下さい。 ***********************************************/ void send_getup_to_soc() { going_to_sleep = false; timeout_sleep = 0; #ifdef _MODEL_CTR_ SLP_ACK = 1; nop8(); SLP_ACK = 0; #endif } /********************************************//** 自己アップデート firm_update() を呼ぶ(isr中から実行できないので) task_status_immed型 を返すようにすればいいのですが... ***********************************************/ task_status_immed tski_firm_update(){ firm_update(); return( ERR_SUCCESS ); } #ifdef _DBG_CHK_OFF_LEAK_ /********************************************//** pin設定ミスチェック ***********************************************/ static void leak_check() { volatile u8 ports_i[12]; volatile u8 ports_o[12]; ports_i[0] = ( PM0 & PU0 & ~P0 ); // プルアップ切り忘れ? ports_i[1] = ( PM1 & PU1 & ~P1 ); ports_i[2] = 0; ports_i[3] = ( PM3 & PU3 & ~P3 ); ports_i[4] = ( PM4 & PU4 & ~P4 ); ports_i[5] = ( PM5 & PU5 & ~P5 ); ports_i[6] = 0; ports_i[7] = ( PM7 & PU7 & ~P7 ); ports_i[8] = ( PM12 & PU12 & ~P12 ); ports_i[9] = ( PM14 & PU14 & ~P14 ); ports_i[10] = 0; ports_i[11] = ( PM20 & PU20 & ~P20 ); ports_o[0] = ( ~PM0 & P0 ); // H 出力しちゃってるかも ports_o[1] = ( ~PM1 & P1 ); ports_o[2] = ( ~PM2 & P2 ); ports_o[3] = ( ~PM3 & P3 & ~POM3 ); ports_o[4] = ( ~PM4 & P4 ); // bit3はchg_enなので1でもよい ports_o[5] = ( ~PM5 & P5 ); ports_o[6] = ( ~PM6 & P6 ); ports_o[7] = ( ~PM7 & P7 ); ports_o[8] = ( ~PM12 & P12 ); ports_o[9] = ( ~PM14 & P14 ); ports_o[10] = ( ~PM15 & P15 ); ports_o[11] = ( ~PM20 & P20 ); NOP(); // ←ブレーク置くため } #endif _DBG_CHK_OFF_LEAK_