/* ======================================================== LED.c ======================================================== */ #pragma sfr #include "incs.h" #include "led.h" // ======================================================== // TPS0 #define BIT_PRS012 ( 1 << 2 ) #define BIT_PRS002 ( 1 << 6 ) // TMR0 #define BIT_CKS0 15 #define BIT_CCS0 12 #define BIT_MASTER0 11 #define BIT_STS0 8 #define BIT_CIS0 6 #define BIT_MD123 1 #define BIT_MD0 0 // ======================================================== // スリープ中明滅のテーブル。マジか。 const u8 LED_PTN_SLEEP[] = { 25, 38, 52, 68, 83, 98, 110, 119, 125, 128, 128, 125, 119, 110, 98, 83, 68, 52, 38, 25, 16, 10, 8, 8, 8, 8, 8, 8, 8, 8, 10, 16 }; #define LED_SLEEP_FRAME_LEN 71 #define LED_SLEEP_DIM_LEN 71 #define LED_SLEEP_FRAME_NUM 32 // ======================================================== static void led_pow_normal( ); static void led_pow_sleep( ); static void led_pow_bt_empty(); static u8 led_pow_batt_low(); // ======================================================== // 赤LEDの電池残量LEDの点滅パターン st_led_red_batt_empty led_red_batt_empty = { 0x55, 0x55, 0x55, 0x55 }; // お知らせLEDのパターンデータ uni_info_LED info_LED; // フルカラーとの自動判別のため、一時領域としてもっておく bit LED_pow_red_Mirror; bit info_led_off; bit info_led_override; bit cam_led_update; // 電源LEDのスリープパターンのステータス類 u8 time_to_next_frame_sleep = LED_SLEEP_FRAME_LEN; u8 frame_sleep; st_LED_dim_status LED_dim_status_sleep; // ======================================================== #define led_fade_to( now, goal ) now = fade_to( now, goal ) /* ======================================================== reg_ledをgoalになるまでグラデーションする とりあえず、ステップ固定 ====================================================== */ u8 fade_to( u8 now, u8 goal ) { if( now != goal ) { if( now > goal ) { now -= 1; } else { now += 1; } } return( now ); } // ======================================================== #define led_fade_to2( led, status ) \ led = fade_to2( status ) u8 fade_to2( st_LED_dim_status* status ) { if( status->now != status->to ) { if( abs(( status->to - status->now )) > abs(status->delta) ) { status->now += status->delta; } else { status->now = status->to; } } return( status->now / 128 ); } // ======================================================== // ======================================================== void LED_init( ) { /** PWMのセット、とりあえず全部消灯 マスタチャネル:0 (P01:/reset2) マスターは偶数チャネルしかできない スレーブ    1 SLTO。(3D LED?)         2 カメラ         3 WiFi         4 (ピンはRTC32kHz out に使用)         5 充電         6 電源 L         7 電源 H */ INFO_LED_IS_FULLCOLOR_PU = 1; // お知らせLEDを識別 先に上げておく TAU0EN = 1; TPS0 = BIT_PRS012 | BIT_PRS002; // マスタークロックはCK01,8M/2 /2^4 = 250kHz TMR00 = 1 << BIT_CKS0 | 0 << BIT_CCS0 | 1 << BIT_MASTER0 | 0 << BIT_STS0 | 0 << BIT_CIS0 | 0 << BIT_MD123 | 1 << BIT_MD0; TMR01 = TMR02 = TMR03 = TMR04 = TMR05 = TMR06 = TMR07 = 1 << BIT_CKS0 | 0 << BIT_CCS0 | 0 << BIT_MASTER0 | 4 << BIT_STS0 | 0 << BIT_CIS0 | 4 << BIT_MD123 | 1 << BIT_MD0; ISC = 0; TOM0 = 0b0000000011111110; // 出力モード。4はPWM出力しないが1にしないとTO5以降にクロックが届かない TOL0 = 0b0000000000000000; // 出力を反転させるかフラグ TO0 = 0; // タイマー動作中で、タイマー出力にしてないときのピンのラッチ。タイマー出力を使わないなら0 TOE0 = 0b0000000011101110; // TOxをタイマーモジュールが制御? TS0 = 0b0000000011101111; // 動作開始 TDR00 = LED_BRIGHT_MAX - 1; // 周期 10bit // お知らせLEDを識別 // // INFO_LED_IS_FULLCOLOR_PU = 1; // もうちょっと前に上げておかないと… system_status.info_fullcolor = 1; if( system_status.model == MODEL_TS_BOARD ) { #ifndef _DBG_FORCE_FULLCOLOR_ system_status.info_fullcolor = 0; #endif } else { if( INFO_LED_IS_FULLCOLOR_n ) { system_status.info_fullcolor = 0; } } INFO_LED_IS_FULLCOLOR_PU = 0; LED_duty_notify_red = 0; LED_duty_notify_blu = 0; LED_duty_notify_grn = 0; LED_pow_red = 0; LED_CAM = 0; if( system_status.reboot ) { vreg_ctr[VREG_C_LED_POW] = LED_POW_ILM_AUTO; LED_duty_pow_blu = LED_BRIGHT_MAX; } info_led_off = false; } void LED_stop( ) { TT0 = 0b0000000011101111; // 一斉停止(しないとだめ) TOE0 = 0b0000000000000000; // TOxをタイマーモジュールが制御?(GPIOになる) TAU0EN = 0; LED_pow_red = 0; LED_CAM = 0; LED_old_pow_red = 0; } /* ======================================================== // 電源LED LED_POW_B,R 6,7 TDR00 周期(0x03FF。TPS0で250kHzでカウントアップ。10bitなら250Hz位になる) TDR0x Duty 0で消灯、TDR00(より大 =0x03FF以上)で点灯です。 enum pwr_state_{ OFF_TRIG = 0, OFF, ON_TRIG, ON, SLEEP }; enum LED_ILUM_MODE{ LED_POW_ILM_AUTO, LED_POW_ILM_ON, LED_POW_ILM_SLEEP, LED_POW_ILM_CEOFF }; ======================================================== */ void tsk_led_pow( ) { info_led_override = false; switch ( vreg_ctr[VREG_C_LED_POW] ) { case ( LED_POW_ILM_AUTO ): default: led_pow_normal( ); break; case ( LED_POW_ILM_SLEEP ): led_pow_sleep( ); break; case ( LED_POW_ILM_ON ): led_fade_to( LED_duty_pow_blu, vreg_ctr[VREG_C_LED_BRIGHT] ); LED_pow_red_Mirror = 0; break; case ( LED_POW_ILM_OFF ): led_fade_to( LED_duty_pow_blu, 0 ); LED_pow_red_Mirror = 0; break; case ( LED_POW_ILM_ONLY_RED ): LED_duty_pow_blu = 0; LED_pow_red_Mirror = 1; break; case ( LED_POW_ILM_ONLY_BLUE ): LED_duty_pow_blu = LED_BRIGHT_MAX; LED_pow_red_Mirror = 0; break; case ( LED_POW_ILM_FORCE_BT_EMPTY ): led_pow_bt_empty(); break; } // 実際にLEDの更新 if( system_status.info_fullcolor ) { LED_pow_red = LED_pow_red_Mirror; } else { LED_old_pow_red = LED_pow_red_Mirror; } if( info_led_override ) { if( system_status.info_fullcolor ) { LED_duty_notify_blu = 0; LED_duty_notify_grn = 0; LED_duty_notify_red = LED_pow_red_Mirror ? 255: 0; } else { LED_duty_old_NOTIFY = LED_pow_red_Mirror ? 255: 0; } } } /* ======================================================== 電池残量で、 青→赤→赤点滅 ======================================================== */ static void led_pow_normal( ) { time_to_next_frame_sleep = LED_SLEEP_FRAME_LEN; frame_sleep = 0; LED_dim_status_sleep.now = (sx16)LED_duty_pow_blu * 128; if( led_pow_batt_low() != 0 ) { return; // おしまい } // 青点灯 led_fade_to( LED_duty_pow_blu, vreg_ctr[VREG_C_LED_BRIGHT] ); } /* ======================================================== ホタルパターン 電池残量で赤→赤点滅にする ======================================================== */ static void led_pow_sleep( ) { if( led_pow_batt_low() != 0 ) { time_to_next_frame_sleep = LED_SLEEP_FRAME_LEN; frame_sleep = 0; LED_dim_status_sleep.now = (sx16)LED_duty_pow_blu * 128; return; // おしまい } LED_dim_status_sleep.to = LED_PTN_SLEEP[frame_sleep] * 128; // グラデーションのデルタを計算 LED_dim_status_sleep.delta = (( LED_dim_status_sleep.to - LED_dim_status_sleep.now ) ) / LED_SLEEP_DIM_LEN; led_fade_to2( LED_duty_pow_blu, &LED_dim_status_sleep ); // 次のフレームに進める? time_to_next_frame_sleep -= 1; if( time_to_next_frame_sleep == 0 ) { time_to_next_frame_sleep = LED_SLEEP_FRAME_LEN; frame_sleep += 1; if( frame_sleep >= LED_SLEEP_FRAME_NUM -1 ) { frame_sleep = 0; } } } /* ======================================================== 電池が少ないときの共通  返値: 0 電池が少なくなかった      1    少なかったので共通パターンにした ======================================================== */ static u8 led_pow_batt_low() { if( vreg_ctr[VREG_C_BT_REMAIN] > BATT_TH_LO ) { LED_pow_red_Mirror = 0; return 0; // おしまい } if(( vreg_ctr[VREG_C_BT_REMAIN] > BATT_TH_EMPTY ) || !BT_IN_CHG_n ) { // 赤点灯 led_fade_to( LED_duty_pow_blu, 0 ); LED_pow_red_Mirror = 1; } else { led_pow_bt_empty(); } return 1; } /* ========================================================  電池がないパターン   指定パターンを流す   お知らせを上書きしたりする ======================================================== */ static void led_pow_bt_empty() { static u8 delay; static u8 red_blink_poi; info_led_override = true; // 赤点滅 led_fade_to( LED_duty_pow_blu, 0 ); // 赤の点滅パターンも指定できる delay += 1; if( delay < 64 ) // フレームの保持時間稼ぎ { return; } delay = 0; if( led_red_batt_empty.dats[ red_blink_poi / 8 ] & ( 1 << ( red_blink_poi % 8 )) ) { LED_pow_red_Mirror = 1; } else { LED_pow_red_Mirror = 0; } red_blink_poi += 1; if( red_blink_poi >= 32 ) { red_blink_poi = 0; } } /* ======================================================== * 割り込みそのものは使いません * LED_Wifi 3 ======================================================== */ void tsk_led_wifi( ) { static u8 task_interval; static u8 state_wifi_tx; static u8 flag_wifi_TX; if( task_interval-- != 0 ) { return; } // 送信パルスのラッチ if( WIFI_txLatch ) // 割り込みフラグそのものを使ってしまう { WIFI_txLatch = 0; flag_wifi_TX = 1; } if( flag_wifi_TX != 0 ) { vreg_ctr[ VREG_C_STATUS_1 ] |= REG_BIT_WIFI_TX; // 送信パターン switch ( state_wifi_tx ) { case ( 1 ): case ( 3 ): case ( 5 ): LED_duty_WiFi = 0; break; default: LED_duty_WiFi = vreg_ctr[VREG_C_LED_BRIGHT]; } state_wifi_tx++; if( state_wifi_tx == 32 ) // ←点滅後、点灯している期間の長さ { state_wifi_tx = 0; flag_wifi_TX -= 1; } task_interval = 25; return; } else { task_interval = 30; // 送信フラグ待ち vreg_ctr[ VREG_C_STATUS_1 ] &= ~REG_BIT_WIFI_TX; if( vreg_ctr[VREG_C_LED_WIFI] == WIFI_LED_OFF ) { LED_duty_WiFi = 0; return; } else { if( LED_duty_WiFi == vreg_ctr[VREG_C_LED_BRIGHT] ) { return; } else if( LED_duty_WiFi < vreg_ctr[VREG_C_LED_BRIGHT] ) { LED_duty_WiFi += 1; } else { LED_duty_WiFi -= 1; } } task_interval = 3; return; } } bit info_led_pattern_updated; /* ======================================================== お知らせLED ======================================================== */ void tsk_led_notify( ) { static u8 time_to_next_frame; static u8 frame; static u8 loops; static st_LED_dim_status LED_dim_status_info_R, LED_dim_status_info_G, LED_dim_status_info_B; if( info_led_override ) { // 電池切れが優先する return; } if( system_status.pwr_state == ON_TRIG ) { LED_duty_notify_blu = 0; LED_duty_notify_red = 0; LED_duty_notify_grn = 0; LED_dim_status_info_R.now = 0; LED_dim_status_info_G.now = 0; LED_dim_status_info_B.now = 0; } if( info_led_off ) // ←電源off時など強制off { // フェードアウトさせる LED_dim_status_info_R.to = 0; LED_dim_status_info_G.to = 0; LED_dim_status_info_B.to = 0; LED_dim_status_info_R.delta = ( 0 - LED_dim_status_info_R.now ) / 64; LED_dim_status_info_G.delta = ( 0 - LED_dim_status_info_G.now ) / 64; LED_dim_status_info_B.delta = ( 0 - LED_dim_status_info_B.now ) / 64; } else { // 通常運転 if( info_led_pattern_updated ) { info_led_pattern_updated = false; vreg_ctr[ VREG_C_LED_NOTIFY_FLAG ] &= ~REG_BIT_IN_LOOP; frame = 0; // ちゃんと書こう time_to_next_frame = 0; } else { // 次のフレームに進める? if( time_to_next_frame == 0 ) { DBG_LED_on; time_to_next_frame = info_LED.info_LED.term; if( frame >= NOTIFY_LED_TERM -1 ) { DBG_LED2_on; vreg_ctr[ VREG_C_LED_NOTIFY_FLAG ] |= REG_BIT_IN_LOOP; if( info_LED.info_LED.last_loop != 255 ) // 255:無限ループ { loops++; if( loops > info_LED.info_LED.last_loop ) { frame = 0; } } } else { frame = (( frame + 1 ) & 0x1F ); // ←ここでマスクをかけておかないと最終フレーム〜先頭間のグラデが効かない vreg_ctr[ VREG_C_LED_NOTIFY_FLAG ] &= ~REG_BIT_IN_LOOP; loops = 0; } LED_dim_status_info_R.to = info_LED.info_LED.red[frame] * 128; LED_dim_status_info_G.to = info_LED.info_LED.grn[frame] * 128; LED_dim_status_info_B.to = info_LED.info_LED.blu[frame] * 128; // グラデーションのデルタを計算 LED_dim_status_info_R.delta = (( LED_dim_status_info_R.to - LED_dim_status_info_R.now ) ) / info_LED.info_LED.fade_time; LED_dim_status_info_G.delta = (( LED_dim_status_info_G.to - LED_dim_status_info_G.now ) ) / info_LED.info_LED.fade_time; LED_dim_status_info_B.delta = (( LED_dim_status_info_B.to - LED_dim_status_info_B.now ) ) / info_LED.info_LED.fade_time; } DBG_LED_off; DBG_LED2_off; time_to_next_frame -= 1; } } if( system_status.info_fullcolor ) { led_fade_to2( LED_duty_notify_blu, &LED_dim_status_info_B ); led_fade_to2( LED_duty_notify_red, &LED_dim_status_info_R ); led_fade_to2( LED_duty_notify_grn, &LED_dim_status_info_G ); } else { led_fade_to2( LED_duty_old_NOTIFY, &LED_dim_status_info_B ); } } /******************************************************//** LED_Cam TO02 \n BLINK,*_PLUSE の時は、1周期分は必ずその状態になります。 \n その間に OFF→BLINK などされると、OFFが無視されます。 *********************************************************/ void tsk_led_cam( ) { static u8 state_led_cam = 0; static u8 task_interval; u8 LED_CAM_mirror; if( !cam_led_update ) { if( task_interval != 0 ) { task_interval -= 1; return; } } cam_led_update = false; // TWL のブリンク設定(一発だけ点灯)のため // ブリンクのように待たせたいとき以外は毎週起動する // (レジスタの変更にすぐに反応する) switch ( vreg_ctr[VREG_C_LED_CAM] ) { case ( CAM_LED_OFF ): default: LED_CAM_mirror = 0; state_led_cam = 0; break; case ( CAM_LED_ON ): LED_CAM_mirror = 1; state_led_cam = 0; break; case ( CAM_LED_BLINK ): if( state_led_cam == 0 ) { LED_CAM_mirror = 1; state_led_cam = 1; } else { LED_CAM_mirror = 0; state_led_cam = 0; } task_interval = 250; break; case ( CAM_LED_ON_PLUSE ): if( state_led_cam == 0 ) { LED_CAM_mirror = 1; state_led_cam = 1; task_interval = 250; } else { vreg_ctr[VREG_C_LED_CAM] = CAM_LED_OFF; } break; case ( CAM_LED_OFF_PLUSE ): if( state_led_cam == 0 ) { LED_CAM_mirror = 0; state_led_cam = 1; task_interval = 250; } else { vreg_ctr[VREG_C_LED_CAM] = CAM_LED_ON; } break; case( CAM_LED_BY_TWL ): switch ( vreg_twl[ REG_TWL_INT_ADRS_CAM ] ){ // switchのネストとか… case( TWL_CAMLED_OFF ): LED_CAM_mirror = 0; state_led_cam = 0; break; case( TWL_CAMLED_BLINK ): if( state_led_cam == 0 ) { LED_CAM_mirror = 1; state_led_cam = 1; } else { LED_CAM_mirror = 0; state_led_cam = 0; } task_interval = 250; break; case( TWL_CAMLED_ON ): case( TWL_CAMLED_DEF_ON ): default: LED_CAM_mirror = 1; state_led_cam = 1; break; } } // 輝度更新 // if( system_status.info_fullcolor ) { LED_CAM = LED_CAM_mirror; } else { LED_old_CAM = LED_CAM_mirror; } }