/* ======================================================== 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 // ======================================================== static void led_pow_normal( ); static void led_pow_hotaru( ); static void led_pow_bt_empty(); // ======================================================== // お知らせLEDのパターンデータ uni_info_LED info_LED; // 赤LEDの電池残量LEDの点滅パターン st_led_red_batt_empty led_red_batt_empty; u8* p_LED_duty_WiFi; // フルカラーとの自動判別のため、一時領域としてもっておく u8 LED_duty_pow_blu_Mirror = 0; bit LED_pow_red_Mirror; bit info_led_off; bit info_led_override; bit cam_led_update; // ======================================================== #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 */ 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以降にクロックが届かない #ifdef _MCU_BSR_ TOL0 = 0b0000000000000000; // 出力を反転させるかフラグ #else TOL0 = 0b0000000000000100; // 出力を反転させるかフラグ #endif TO0 = 0; // タイマー動作中で、タイマー出力にしてないときのピンのラッチ。タイマー出力を使わないなら0 TOE0 = 0b0000000011101010; // TOxをタイマーモジュールが制御? TS0 = 0b0000000011101111; // 動作開始 TDR00 = LED_BRIGHT_MAX - 1; // 周期 10bit // お知らせLEDを識別 // system_status.info_fullcolor = 0; p_LED_duty_WiFi = &LED_duty_old_WiFi; if( system_status.model == MODEL_TS_BOARD ) { #ifdef _FORCE_INFO_LED_FULLCOLOR_ // todo debug // system_status.info_fullcolor = 1; p_LED_duty_WiFi = &LED_duty_WiFi; #endif } else { INFO_LED_IS_FULLCOLOR_PU = 1; if( !INFO_LED_IS_FULLCOLOR_n ) { system_status.info_fullcolor = 1; p_LED_duty_WiFi = &LED_duty_WiFi; } INFO_LED_IS_FULLCOLOR_PU = 0; } if( system_status.reboot ) { vreg_ctr[VREG_C_LED_POW] = LED_POW_ILM_AUTO; LED_duty_pow_blu_Mirror = LED_BRIGHT_MAX; if( system_status.info_fullcolor ) { LED_duty_pow_blu = LED_duty_pow_blu_Mirror; } else { LED_duty_old_pow_blu = LED_duty_pow_blu_Mirror; } } info_led_off = 0; } void LED_stop( ) { TT0 = 0b0000000011101111; // 一斉停止(しないとだめ) TOE0 = 0b0000000000000000; // TOxをタイマーモジュールが制御?(GPIOになる) TAU0EN = 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_TRIG, SLEEP }; enum LED_ILUM_MODE{ LED_POW_ILM_AUTO, LED_POW_ILM_ON, LED_POW_ILM_HOTARU, LED_POW_ILM_CEOFF }; ======================================================== */ void tsk_led_pow( ) { info_led_override = 0; switch ( vreg_ctr[VREG_C_LED_POW] ) { case ( LED_POW_ILM_AUTO ): switch ( system_status.pwr_state ) { case SLEEP: led_pow_hotaru( ); break; case ON: led_pow_normal( ); break; default: break; } break; case ( LED_POW_ILM_HOTARU ): led_pow_hotaru( ); break; case ( LED_POW_ILM_ON ): default: led_pow_normal( ); break; case ( LED_POW_ILM_OFF ): led_fade_to( LED_duty_pow_blu_Mirror, 0 ); LED_pow_red_Mirror = 0; break; case ( LED_POW_ILM_ONLY_RED ): LED_duty_pow_blu_Mirror = 0; LED_pow_red_Mirror = 1; break; case ( LED_POW_ILM_ONLY_BLUE ): LED_duty_pow_blu_Mirror = 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_duty_pow_blu = LED_duty_pow_blu_Mirror; LED_pow_red = LED_pow_red_Mirror; } else { LED_duty_old_pow_blu = LED_duty_pow_blu_Mirror; 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( ) { if( vreg_ctr[VREG_C_BT_REMAIN] <= BATT_TH_EMPTY ) { led_pow_bt_empty(); } else if( vreg_ctr[VREG_C_BT_REMAIN] <= BATT_TH_LO ) { // 赤点灯 led_fade_to( LED_duty_pow_blu_Mirror, 0 ); LED_pow_red_Mirror = 1; } else { // 青点灯 led_fade_to( LED_duty_pow_blu_Mirror, vreg_ctr[VREG_C_LED_BRIGHT] ); LED_pow_red_Mirror = 0; } } static void led_pow_bt_empty() { static u8 delay; static u8 red_blink_poi; info_led_override = 1; // 赤点滅 led_fade_to( LED_duty_pow_blu_Mirror, 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; } } /* ======================================================== ホタルパターン ======================================================== */ static void led_pow_hotaru( ) { static u8 delay; static u8 state; static u16 blue_to; if( delay != 0 ) { delay -= 1; return; } delay = 4; switch ( state ) { // フェードイン case ( 0 ): case ( 2 ): case ( 4 ): if( vreg_ctr[VREG_C_BT_REMAIN] <= BATT_TH_LO ) { blue_to = 0; LED_pow_red_Mirror = 1; } else { blue_to = vreg_ctr[VREG_C_LED_BRIGHT]; LED_pow_red_Mirror = 0; } break; default: // フェードアウト if( vreg_ctr[VREG_C_BT_REMAIN] <= BATT_TH_LO ) { LED_pow_red_Mirror = 0; } else { blue_to = 2; } break; } // LED更新 if( LED_duty_pow_blu_Mirror != blue_to ) { if( LED_duty_pow_blu_Mirror > blue_to ) { LED_duty_pow_blu_Mirror -= 1; } else { LED_duty_pow_blu_Mirror += 1; } } if( LED_duty_pow_blu_Mirror == blue_to ) { state += 1; } return; } /* ======================================================== * 割り込みそのものは使いません * 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 = 2; } 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 ): *p_LED_duty_WiFi = 0; break; default: *p_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 = 22; 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 ) { if( *p_LED_duty_WiFi == 0 ) { return; } else { *p_LED_duty_WiFi -= 1; } } else { if( *p_LED_duty_WiFi == vreg_ctr[VREG_C_LED_BRIGHT] ) { return; } else { if( *p_LED_duty_WiFi < vreg_ctr[VREG_C_LED_BRIGHT] ) { *p_LED_duty_WiFi += 3; } else { *p_LED_duty_WiFi -= 3; } } } task_interval = 3; return; } } /* ======================================================== お知らせLED ======================================================== */ void tsk_led_notify( ) { // static u8 task_interval; static u8 time_to_next_frame; static u8 frame; static u8 loops_to_go; 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.to = 0; LED_dim_status_info_G.to = 0; LED_dim_status_info_B.to = 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 ) { // 次のフレームに進める? if( time_to_next_frame == 0 ) { time_to_next_frame = info_LED.info_LED.term; if( frame >= NOTIFY_LED_TERM -1 ) { vreg_ctr[ VREG_C_LED_NOTIFY_FLAG ] |= REG_BIT_IN_LOOP; if( info_LED.info_LED.last_loop != 255 ) // 255:無限ループ { if( loops_to_go != 0 ) { loops_to_go -= 1; } else { loops_to_go = info_LED.info_LED.last_loop; frame = 0; } } } else { vreg_ctr[ VREG_C_LED_NOTIFY_FLAG ] &= ~REG_BIT_IN_LOOP; frame = (( frame + 1 ) & 0x1F ); // ←ここでマスクをかけておかないと最終フレーム〜先頭間のグラデが効かない } 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; } time_to_next_frame -= 1; } else { // フェードアウトさせる 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; } 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 = 0; // 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; } }