const char *trimsource_name_cb(guiObject_t *obj, const void *data) { (void)obj; u8 i = (long)data; struct Trim *trim = MIXER_GetAllTrims(); return INPUT_SourceName(tp->tmpstr, MIXER_MapChannel(trim[i].src)); }
static const char *dlgts_cb(guiObject_t *obj, int dir, void *data) { (void)obj; int idx = (long)data; int type = ELEM_TYPE(pc->elem[idx]); switch (type) { case ELEM_SMALLBOX: case ELEM_BIGBOX: { u8 changed = 0; pc->elem[idx].src = GUI_TextSelectHelper(pc->elem[idx].src, 0, NUM_RTC + NUM_TELEM + NUM_TIMERS + NUM_CHANNELS, dir, 1, 1, &changed); if(changed && dir) { pc->elem[idx].src = _adjust_src_for_telemetry(pc->elem[idx].src, dir); } return GetBoxSource(tempstring, pc->elem[idx].src); } case ELEM_BAR: pc->elem[idx].src = GUI_TextSelectHelper(pc->elem[idx].src, 0, NUM_CHANNELS, dir, 1, 1, NULL); return INPUT_SourceName(tempstring, pc->elem[idx].src ? pc->elem[idx].src + NUM_INPUTS : 0); case ELEM_TOGGLE: { pc->elem[idx].src = INPUT_SelectAbbrevSource(pc->elem[idx].src, dir); return INPUT_SourceNameAbbrevSwitch(tempstring, pc->elem[idx].src); } case ELEM_HTRIM: case ELEM_VTRIM: pc->elem[idx].src = GUI_TextSelectHelper(pc->elem[idx].src, 0, NUM_TRIMS, dir, 1, 1, NULL); if (pc->elem[idx].src == 0) return _tr("None"); snprintf(tempstring, sizeof(tempstring), "%s%d", _tr("Trim"),pc->elem[idx].src); return tempstring; } return _tr("None"); }
static const char *set_source_cb(guiObject_t *obj, int dir, void *data) { (void) obj; u8 *source = (u8 *)data; *source = GUI_TextSelectHelper(MIXER_SRC(*source), 0, NUM_SOURCES, dir, 1, 1, NULL); return INPUT_SourceName(tp->tmpstr, MIXER_MapChannel(*source)); }
static const char *channum_cb(guiObject_t *obj, const void *data) { (void)obj; long ch = (long)data; INPUT_SourceName(tempstring, ch+1); return tempstring; }
static const char *safety_string_cb(guiObject_t *obj, void *data) { (void)data; int i; u32 crc = Crc(tempstring, strlen(tempstring)); if (obj && crc == dialogcrc) return tempstring; int count = 0; const s8 safeval[4] = {0, -100, 0, 100}; volatile s16 *raw = MIXER_GetInputs(); u64 unsafe = PROTOCOL_CheckSafe(); tempstring[0] = 0; for(i = 0; i < NUM_SOURCES + 1; i++) { if (! (unsafe & (1LL << i))) continue; int ch = (i == 0) ? PROTOCOL_MapChannel(INP_THROTTLE, NUM_INPUTS + 2) : i-1; s16 val = RANGE_TO_PCT((ch < NUM_INPUTS) ? raw[ch+1] : MIXER_GetChannel(ch - (NUM_INPUTS), APPLY_SAFETY)); INPUT_SourceName(tempstring + strlen(tempstring), ch + 1); int len = strlen(tempstring); snprintf(tempstring + len, sizeof(tempstring) - len, _tr(" is %d%%, safe value = %d%%\n"), val, safeval[Model.safety[i]]); if (++count >= 5) break; } return tempstring; }
const char *MIXPAGE_ChanNameProtoCB(guiObject_t *obj, const void *data) { (void)obj; u8 ch = (long)data; u8 proto_map_length = PROTO_MAP_LEN; char tmp1[30]; /* See if we need to name the cyclic virtual channels */ if (_is_virt_cyclic(ch)) { switch(ch - NUM_OUT_CHANNELS) { case 0: snprintf(tempstring, sizeof(tempstring), "%s-%s", _tr("CYC"), _tr("AIL")); return tempstring; case 1: snprintf(tempstring, sizeof(tempstring), "%s-%s", _tr("CYC"), _tr("ELE")); return tempstring; case 2: snprintf(tempstring, sizeof(tempstring), "%s-%s", _tr("CYC"), _tr("COL")); return tempstring; } } #if defined(_DEVO7E_256_TARGET_H_) || defined(_T8SG_TARGET_H_) #define SWITCH_NOSTOCK ((1 << INP_HOLD0) | (1 << INP_HOLD1) | \ (1 << INP_FMOD0) | (1 << INP_FMOD1)) if ((Transmitter.ignore_src & SWITCH_NOSTOCK) == SWITCH_NOSTOCK) proto_map_length = PROTO_MAP_LEN - 1; #endif if (ch < proto_map_length && ProtocolChannelMap[Model.protocol]) { INPUT_SourceNameAbbrevSwitch(tmp1, ProtocolChannelMap[Model.protocol][ch]); sprintf(tempstring, "%s%d-%s", (Model.limits[ch].flags & CH_REVERSE) ? "!" : "", (int)(ch + 1), tmp1); } else { INPUT_SourceName(tmp1, ch + NUM_INPUTS + 1); sprintf(tempstring, "%s%s", (ch < Model.num_channels && Model.limits[ch].flags & CH_REVERSE) ? "!" : "", tmp1); } return tempstring; }
const char *set_drsource_cb(guiObject_t *obj, int dir, void *data) { (void) obj; u8 *source = (u8 *)data; u8 is_neg = MIXER_SRC_IS_INV(*source); u8 changed; u8 oldsrc = *source; *source = GUI_TextSelectHelper(MIXER_SRC(*source), 0, NUM_SOURCES, dir, 1, 1, &changed); MIXER_SET_SRC_INV(*source, is_neg); if (changed) { sync_mixers(); if ((!! MIXER_SRC(oldsrc)) ^ (!! MIXER_SRC(*source))) { // bug fix (issues #191) : only invoke _update_rate_widgets() for expo template if (mp->cur_template == MIXERTEMPLATE_EXPO_DR) { if(data == &mp->mixer[1].sw) _update_rate_widgets(0); else if(data == &mp->mixer[2].sw) _update_rate_widgets(1); } } else { MIXPAGE_RedrawGraphs(); } } GUI_TextSelectEnablePress((guiTextSelect_t *)obj, MIXER_SRC(*source)); return INPUT_SourceName(mp->tmpstr, *source); }
const char *set_source_cb(guiObject_t *obj, int dir, void *data) { (void) obj; u8 *source = (u8 *)data; if (!GUI_IsTextSelectEnabled(obj) ) { strcpy(mp->tmpstr, _tr("None")); return mp->tmpstr; } u8 is_neg = MIXER_SRC_IS_INV(*source); u8 changed; *source = GUI_TextSelectHelper(MIXER_SRC(*source), 1, NUM_SOURCES, dir, 1, 1, &changed); MIXER_SET_SRC_INV(*source, is_neg); if (changed) { if(mp->cur_template == MIXERTEMPLATE_COMPLEX) { guiObject_t *trim = _get_obj(COMPLEX_TRIM, 0); if(trim) { if (MIXER_SourceHasTrim(MIXER_SRC(mp->mixer[0].src))) GUI_SetHidden(trim, 0); else GUI_SetHidden(trim, 1); } } sync_mixers(); MIXPAGE_RedrawGraphs(); } GUI_TextSelectEnablePress((guiTextSelect_t *)obj, MIXER_SRC(*source)); return INPUT_SourceName(mp->tmpstr, *source); }
static const char *sourcesel_cb(guiObject_t *obj, int dir, void *data) { (void)obj; (void)data; u8 changed; dlog->enable = GUI_TextSelectHelper(dlog->enable, 0, NUM_SOURCES, dir, 1, 1, &changed); if (changed) DATALOG_UpdateState(); return INPUT_SourceName(tempstring, dlog->enable); }
static const char *_channum_cb(guiObject_t *obj, const void *data) { (void)obj; long ch = (long)data; if (cp->type == MONITOR_RAWINPUT) { INPUT_SourceName(cp->tmpstr, ch+1); } else { sprintf(cp->tmpstr, "%d", (int)ch+1); } return cp->tmpstr; }
const char *GetBoxSource(char *str, int src) { if (src) { #if HAS_RTC if (src <= NUM_RTC) return RTC_Name(str, src - 1); #endif if (src - NUM_RTC <= NUM_TIMERS) return TIMER_Name(str, src - NUM_RTC - 1); else if( src - NUM_RTC - NUM_TIMERS <= NUM_TELEM) return TELEMETRY_Name(str, src - NUM_RTC - NUM_TIMERS); } return INPUT_SourceName(str, src ? src - (NUM_TELEM + NUM_TIMERS + NUM_RTC) + NUM_INPUTS : 0); }
void debug_switches() { s32 data[INP_LAST]; for(int i = INP_HAS_CALIBRATION+1; i < INP_LAST; i++) { data[i] = CHAN_ReadRawInput(i); } while(1) { u32 changed = 0; for(int i = INP_HAS_CALIBRATION+1; i < INP_LAST; i++) { s32 val = CHAN_ReadRawInput(i); if (val != data[i]) { printf("%s=%d ", INPUT_SourceName(tempstring, i), val); data[i] = val; changed = 1; } } if (changed) { printf("\n"); } if(PWR_CheckPowerSwitch()) PWR_Shutdown(); } }
const char *_set_src_cb(guiTextSelect_t *obj, u8 *src, int dir, int idx, int source) { u8 changed; if (Model.mixer_mode == MIXER_STANDARD && Model.type == MODELTYPE_HELI) { //Improvement: only to intelligent switch setting for heli type in standard mode int is_neg = MIXER_SRC_IS_INV(*src); int step = mapped_std_channels.throttle + NUM_INPUTS +1; int newsrc = GUI_TextSelectHelper(MIXER_SRC(*src), 0, step, dir, step, step, &changed); MIXER_SET_SRC_INV(newsrc, is_neg); *src = newsrc; } else { if (source <= INP_NONE) *src = INPUT_SelectSource(*src, dir, &changed); else *src = INPUT_SelectInput(*src, source, &changed); } if (changed) { TIMER_Reset(idx); } GUI_TextSelectEnablePress(obj, MIXER_SRC(*src)); return INPUT_SourceName(tempstring, *src); }
static const char *channum_cb(guiObject_t *obj, const void *data) { (void)obj; long ch = (long)data; if (cp->type) { char *p = cp->tmpstr; if (ch & 0x01) { *p = '\n'; p++; } CONFIG_EnableLanguage(0); //Disable translation because tiny font is limitied in character set INPUT_SourceName(p, ch+1); CONFIG_EnableLanguage(1); if (! (ch & 0x01)) { sprintf(p + strlen(p), "\n"); } } else { sprintf(cp->tmpstr, "\n%d", (int)ch+1); } return cp->tmpstr; }
static const char *set_source_helper(guiObject_t *obj, void *data, int changed) { (void) obj; u8 *source = (u8 *)data; if (!GUI_IsTextSelectEnabled(obj) ) { tempstring_cpy(_tr("None")); return tempstring; } if (changed) { if(mp->cur_template == MIXERTEMPLATE_COMPLEX) { guiObject_t *trim = _get_obj(COMPLEX_TRIM, 0); if(trim) { if (MIXER_SourceHasTrim(MIXER_SRC(mp->mixer[0].src))) GUI_SetHidden(trim, 0); else GUI_SetHidden(trim, 1); } } sync_mixers(); MIXPAGE_RedrawGraphs(); } GUI_TextSelectEnablePress((guiTextSelect_t *)obj, MIXER_SRC(*source)); return INPUT_SourceName(tempstring, *source); }
const char *DATALOG_Source(char *str, int idx) { #if HAS_RTC if (idx == DLOG_TIME) { strcpy(str, _tr("RTC Time")); } else #endif if (idx == DLOG_GPSTIME) { strcpy(str, _tr("GPS Time")); } else if (idx == DLOG_GPSLOC) { strcpy(str, _tr("GPS Coords")); } else if (idx == DLOG_GPSALT) { strcpy(str, _tr("GPS Alt.")); } else if (idx == DLOG_GPSSPEED) { strcpy(str, _tr("GPS Speed")); } else if (idx >= DLOG_INPUTS) { return INPUT_SourceName(str, idx - DLOG_INPUTS + 1); } else if (idx >= DLOG_TELEMETRY) { return TELEMETRY_Name(str, idx - DLOG_TELEMETRY + 1); } else { // idx >= DLOG_TIMERS return TIMER_Name(str, idx); } return str; }
static const char *dlgts_cb(guiObject_t *obj, int dir, void *data) { (void)obj; int idx = (long)data; int type = ELEM_TYPE(pc.elem[idx]); switch (type) { case ELEM_SMALLBOX: case ELEM_BIGBOX: { pc.elem[idx].src = GUI_TextSelectHelper(pc.elem[idx].src, 0, NUM_RTC + NUM_TELEM + NUM_TIMERS + NUM_CHANNELS, dir, 1, 1, NULL); return GetBoxSource(lp.tmp, pc.elem[idx].src); } case ELEM_BAR: pc.elem[idx].src = GUI_TextSelectHelper(pc.elem[idx].src, 0, NUM_CHANNELS, dir, 1, 1, NULL); return INPUT_SourceName(lp.tmp, pc.elem[idx].src ? pc.elem[idx].src + NUM_INPUTS : 0); case ELEM_TOGGLE: { int val = MIXER_SRC(pc.elem[idx].src); int newval = GUI_TextSelectHelper(val, 0, NUM_SOURCES, dir, 1, 1, NULL); newval = INPUT_GetAbbrevSource(val, newval, dir); if (val != newval) { val = newval; pc.elem[idx].src = val; } return INPUT_SourceNameAbbrevSwitch(lp.tmp, pc.elem[idx].src); } case ELEM_HTRIM: case ELEM_VTRIM: pc.elem[idx].src = GUI_TextSelectHelper(pc.elem[idx].src, 0, NUM_TRIMS, dir, 1, 1, NULL); if (pc.elem[idx].src == 0) return _tr("None"); sprintf(lp.tmp, "%s%d", _tr("Trim"),pc.elem[idx].src); return lp.tmp; } return ""; }
static void show_button_page() { // show elements where they are located on the real tx enum { OFFSET_X = ((LCD_WIDTH - 320) / 2), // center on Devo12-screen OFFSET_Y = ((LCD_HEIGHT - 240) / 2), }; enum {X = 0, Y = 1}; struct LabelDesc alignRight = { .font = DEFAULT_FONT.font, .align = ALIGN_RIGHT, .font_color = DEFAULT_FONT.font_color, .fill_color = DEFAULT_FONT.fill_color, .outline_color = DEFAULT_FONT.outline_color }; const int label_pos[NUM_TX_BUTTONS][2] = CHANTEST_BUTTON_PLACEMENT; cp->is_locked = 3; GUI_CreateLabelBox(&gui->lock, OFFSET_X, 34, 320, 20, &NARROW_FONT, lockstr_cb, NULL, NULL); for (int i = 0; i < NUM_TX_BUTTONS; i++) { if ((1 << (i + 1)) & Transmitter.ignore_buttons) continue; GUI_CreateLabelBox(&gui->value[i], OFFSET_X + (label_pos[i][X] > 0 ? label_pos[i][X] + 50 : -label_pos[i][X] -20), // >0? box at left side of label, otherwise right OFFSET_Y + label_pos[i][Y], 16, 16, &SMALLBOX_FONT, NULL, NULL, (void *)""); GUI_CreateLabelBox(&gui->chan[i], OFFSET_X + abs(label_pos[i][X]), // no differencing for the label OFFSET_Y + label_pos[i][Y], 48, 16, label_pos[i][X] > 0 ? &alignRight : &DEFAULT_FONT, button_str_cb, NULL, (void *)(long)i); } } void _handle_button_test() { if (cp->is_locked == 0 && SPITouch_IRQ()) { BUTTON_RegisterCallback(&cp->action, 0xFFFFFFFF, BUTTON_PRESS | BUTTON_RELEASE | BUTTON_LONGPRESS | BUTTON_PRIORITY, button_capture_cb, NULL); GUI_Redraw(&gui->lock); //Textbox cp->is_locked++; } else if (cp->is_locked == 1 && ! SPITouch_IRQ()) { cp->is_locked++; } else if (cp->is_locked == 2 && SPITouch_IRQ()) { BUTTON_UnregisterCallback(&cp->action); GUI_Redraw(&gui->lock); //Textbox cp->is_locked++; } else if (cp->is_locked == 3 && ! SPITouch_IRQ()) { cp->is_locked = 0; } u32 buttons = ScanButtons(); for (int i = 0; i < NUM_TX_BUTTONS; i++) { GUI_SetLabelDesc(&gui->value[i], CHAN_ButtonIsPressed(buttons, i+1) ? &SMALLBOXNEG_FONT : &SMALLBOX_FONT); } return; } static inline guiObject_t *_get_obj(int chan, int objid) { return objid == ITEM_GRAPH ? (guiObject_t *)&gui->bar[chan] : (guiObject_t *)&gui->value[chan]; } static const char *channum_cb(guiObject_t *obj, const void *data) { (void)obj; int disp = (long)data; int ch = get_channel_idx(cur_row * NUM_BARS_PER_ROW + disp); if (cp->type) { char *p = tempstring; if (disp & 0x01) { *p = '\n'; p++; } CONFIG_EnableLanguage(0); //Disable translation because tiny font is limited in character set INPUT_SourceName(p, ch+1); CONFIG_EnableLanguage(1); if (! (disp & 0x01)) { sprintf(p + strlen(p), "\n"); } } else { ch -= NUM_INPUTS; if (ch < NUM_OUT_CHANNELS) { sprintf(tempstring, "\n%d", ch+1); } else { ch -= NUM_OUT_CHANNELS; if (Model.virtname[ch][0]) { tempstring_cpy(Model.virtname[ch]) ; } else { sprintf(tempstring, "%s%d", _tr("Virt"), ch+1); } } } return tempstring; }
const char *MIXPAGE_ChannelNameCB(guiObject_t *obj, const void *data) { (void)obj; return INPUT_SourceName(tempstring, (long)data + NUM_INPUTS + 1); }
void EventLoop() { CLOCK_ResetWatchdog(); #ifdef HEAP_DEBUG static int heap = 0; int h = _sbrk_r(NULL, 0); if(h > heap) { printf("heap: %x\n", h); heap = h; } #endif #ifdef TIMING_DEBUG debug_timing(0, 0); #endif priority_ready &= ~(1 << MEDIUM_PRIORITY); #if !HAS_HARD_POWER_OFF if(PWR_CheckPowerSwitch()) { if(! (BATTERY_Check() & BATTERY_CRITICAL)) { PAGE_Test(); CONFIG_SaveModelIfNeeded(); CONFIG_SaveTxIfNeeded(); } if(Transmitter.music_shutdown) { #if HAS_EXTENDED_AUDIO if(AUDIO_VoiceAvailable()) { MUSIC_Play(MUSIC_SHUTDOWN); while (CLOCK_getms() < audio_queue_time) { // Wait for voice to finished CLOCK_ResetWatchdog(); } } else { #else { // We wait ~1sec for shutdown buzzer music finished unsigned int time; MUSIC_Play(MUSIC_SHUTDOWN); time = CLOCK_getms()+700; while (CLOCK_getms() < time) { CLOCK_ResetWatchdog(); } #endif } } PWR_Shutdown(); } #endif BUTTON_Handler(); TOUCH_Handler(); INPUT_CheckChanges(); if (priority_ready & (1 << LOW_PRIORITY)) { priority_ready &= ~(1 << LOW_PRIORITY); PAGE_Event(); PROTOCOL_CheckDialogs(); TIMER_Update(); TELEMETRY_Alarm(); BATTERY_Check(); AUTODIMMER_Update(); #if HAS_DATALOG DATALOG_Update(); #endif #if HAS_VIDEO VIDEO_Update(); #endif #if HAS_EXTENDED_AUDIO AUDIO_CheckQueue(); #endif GUI_RefreshScreen(); #if HAS_HARD_POWER_OFF if (PAGE_ModelDoneEditing()) CONFIG_SaveModelIfNeeded(); CONFIG_SaveTxIfNeeded(); #endif } #ifdef TIMING_DEBUG debug_timing(0, 1); #endif } void TOUCH_Handler() { if(! HAS_TOUCH) return; u32 pen_down=0; static u32 pen_down_last=0; static u32 pen_down_long_at=0; struct touch t; if(SPITouch_IRQ()) { pen_down=1; t=SPITouch_GetCoords(); if (! pen_down_last) pen_down_long_at=CLOCK_getms()+500; } else { pen_down=0; } if(pen_down && (!pen_down_last)) { AUTODIMMER_Check(); GUI_CheckTouch(&t, 0); } if(!pen_down && pen_down_last) { GUI_TouchRelease(); } if(pen_down && pen_down_last) { if(CLOCK_getms()>pen_down_long_at) { GUI_CheckTouch(&t, 1); pen_down_long_at += 100; } } pen_down_last=pen_down; } #if HAS_VIDEO void VIDEO_Update() { static u8 video_enable = 0; static u32 check_standard_ms = 0; // Check if Video is turn on int enabled = MIXER_SourceAsBoolean(Model.videosrc); if (enabled != video_enable) { VIDEO_Enable(enabled); video_enable = enabled; if (enabled) { VIDEO_SetChannel(Model.videoch); VIDEO_Contrast(Model.video_contrast); VIDEO_Brightness(Model.video_brightness); check_standard_ms = CLOCK_getms() + 3000; } else check_standard_ms = 0; } if(video_enable && check_standard_ms > 0 && check_standard_ms < CLOCK_getms()) { u8 video_standard_current = VIDEO_GetStandard(); if((video_standard_current > 0) && (video_standard_current < 8)) { VIDEO_SetStandard(video_standard_current); check_standard_ms = 0; } else { check_standard_ms = CLOCK_getms() + 3000; } } if(video_enable) AUTODIMMER_Check(); } #endif //HAS_VIDEO #ifdef TIMING_DEBUG void debug_timing(u32 type, int startend) { static u32 last_time[2][100]; static u32 loop_time[4][101]; static u32 loop_pos[4] = {-1, -1, -1, -1}; static u32 max_last[2]; static u32 max_loop[4]; static int save_priority; if (type == 0) { if (! startend) save_priority = priority_ready; if (save_priority & (1 << MEDIUM_PRIORITY)) debug_timing(2, startend); if (save_priority & (1 << LOW_PRIORITY)) debug_timing(1, startend); return; } type--; if (! startend) { u32 t = CLOCK_getms(); loop_pos[type] = (loop_pos[type] + 1) % 100; if (type < 2) { last_time[type][loop_pos[type]] = t; if (t - last_time[type][(loop_pos[type] + 99) % 100] > max_last[type]) max_last[type] = t - last_time[type][(loop_pos[type] + 99) % 100]; } loop_time[type][100] = t; } else { loop_time[type][loop_pos[type]] = CLOCK_getms() - loop_time[type][100]; if (loop_time[type][loop_pos[type]] > max_loop[type]) max_loop[type] = loop_time[type][loop_pos[type]]; if (type == 0 && loop_pos[0] == 99) { unsigned avg_loop[4] = {0, 0, 0, 0}; unsigned avg_last[2] = {0, 0}; for(int i = 0; i < 99; i++) { for(int t = 0; t < 2; t++) { u32 delay = last_time[t][(i + loop_pos[t] + 2) % 100] - last_time[t][(i + loop_pos[t] + 1) % 100]; avg_last[t] += delay; } for(int t = 0; t < 4; t++) avg_loop[t] += loop_time[t][i]; } for(int t = 0; t < 4; t++) avg_loop[t] /= 99; avg_last[0] /= 99; avg_last[1] /= 99; printf("Avg: radio: %d mix: %d med: %d/%d low: %d/%d\n", avg_loop[3], avg_loop[2], avg_loop[1], avg_last[1], avg_loop[0], avg_last[0]); printf("Max: radio: %d mix: %d med: %d/%d low: %d/%d\n", max_loop[3], max_loop[2], max_loop[1], max_last[1], max_loop[0], max_last[0]); memset(max_loop, 0, sizeof(max_loop)); max_last[0] = 0; max_last[1] = 0; } } } #endif void debug_switches() { s32 data[INP_LAST]; for(int i = INP_HAS_CALIBRATION+1; i < INP_LAST; i++) { data[i] = CHAN_ReadRawInput(i); } while(1) { u32 changed = 0; for(int i = INP_HAS_CALIBRATION+1; i < INP_LAST; i++) { s32 val = CHAN_ReadRawInput(i); if (val != data[i]) { printf("%s=%d ", INPUT_SourceName(tempstring, i), val); data[i] = val; changed = 1; } } if (changed) { printf("\n"); } if(PWR_CheckPowerSwitch()) PWR_Shutdown(); } }