diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index 0850f989..e31c53d7 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -215,6 +215,11 @@ if(ENABLE_FEAT_F4HWN_BEAM AND NOT ENABLE_AIRCOPY) endif() enable_feature(ENABLE_FEAT_F4HWN_QRCODE) enable_feature(ENABLE_FEAT_F4HWN_LOGO) +enable_feature(ENABLE_FEAT_F4HWN_LOGO_SAV) + +if(ENABLE_FEAT_F4HWN_LOGO_SAV AND NOT ENABLE_FEAT_F4HWN_LOGO) + message(FATAL_ERROR "ENABLE_FEAT_F4HWN_LOGO_SAV requires ENABLE_FEAT_F4HWN_LOGO.") +endif() # ---- DEBUGGING ---- diff --git a/App/app/app.c b/App/app/app.c index f404782e..3847f1ea 100644 --- a/App/app/app.c +++ b/App/app/app.c @@ -72,11 +72,13 @@ #include "sram-overlay.h" #endif #include "ui/battery.h" +#include "ui/helper.h" #include "ui/inputbox.h" #include "ui/main.h" #include "ui/menu.h" #include "ui/status.h" #include "ui/ui.h" +#include "ui/welcome.h" #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #include "screenshot.h" @@ -86,6 +88,16 @@ static bool flagSaveVfo; static bool flagSaveSettings; static bool flagSaveChannel; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV +static bool gScreenSaverDisplayed; +static uint8_t gScreenSaverMode; +static uint8_t gScreenSaverTick; +static uint16_t gMatrixRandom = 0xACE1; +static uint8_t gMatrixHeads[32]; +static uint8_t gMatrixSpeeds[32]; +static KEY_Code_t gScreenSaverWakeKey = KEY_INVALID; +#endif + static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld); @@ -110,11 +122,118 @@ static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM-1); static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM); #endif +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV +static uint8_t ScreenSaverRandom(void) +{ + gMatrixRandom = (uint16_t)(gMatrixRandom * 2053u + 13849u); + return (uint8_t)(gMatrixRandom >> 8); +} + +static void ScreenSaverSetPixel(uint8_t x, uint8_t y, bool fill) +{ + const uint8_t pattern = 1u << (y & 7u); + + if (y < 8) { + if (fill) + gStatusLine[x] |= pattern; + else + gStatusLine[x] &= (uint8_t)~pattern; + return; + } + + y -= 8; + if (fill) + gFrameBuffer[y >> 3][x] |= pattern; + else + gFrameBuffer[y >> 3][x] &= (uint8_t)~pattern; +} + +static void ScreenSaverDrawMatrixGlyph(uint8_t x, int16_t y, uint8_t glyph) +{ + static const uint8_t glyphs[][3] = { + {0x1F, 0x11, 0x1F}, + {0x15, 0x1F, 0x15}, + {0x1D, 0x15, 0x17}, + {0x17, 0x15, 0x1D}, + {0x1F, 0x04, 0x1F}, + {0x1B, 0x15, 0x1B}, + {0x0E, 0x11, 0x0E}, + {0x11, 0x0A, 0x04} + }; + + for (uint8_t dx = 0; dx < 3; dx++) { + uint8_t pixels = glyphs[glyph & 7u][dx]; + for (uint8_t dy = 0; dy < 5; dy++) { + const int16_t py = y + dy; + if ((pixels & (1u << dy)) && py >= 0 && py < LCD_HEIGHT) + ScreenSaverSetPixel(x + dx, (uint8_t)py, true); + } + } +} + +static void ScreenSaverRenderMatrix(bool reset) +{ + if (reset) { + for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) { + gMatrixHeads[i] = ScreenSaverRandom() % 112u; + gMatrixSpeeds[i] = 1u + (ScreenSaverRandom() % 3u); + } + } else { + for (uint8_t i = 0; i < ARRAY_SIZE(gMatrixHeads); i++) { + gMatrixHeads[i] += gMatrixSpeeds[i]; + if (gMatrixHeads[i] > 112u) + gMatrixHeads[i] = ScreenSaverRandom() & 0x0Fu; + } + } + + UI_StatusClear(); + UI_DisplayClear(); + + for (uint8_t col = 0; col < ARRAY_SIZE(gMatrixHeads); col++) { + const uint8_t x = col * 4u; + for (uint8_t trail = 0; trail < 8; trail++) { + const int16_t y = (int16_t)gMatrixHeads[col] - ((int16_t)trail * 7); + const uint8_t glyph = (uint8_t)(col + trail + gMatrixHeads[col]); + + if (trail == 0) { + ScreenSaverDrawMatrixGlyph(x, y, glyph); + } else if ((trail < 4) || ((glyph & 1u) != 0)) { + ScreenSaverDrawMatrixGlyph(x, y, glyph); + } + } + } + + ST7565_BlitStatusLine(); + ST7565_BlitFullScreen(); +} + +static void ScreenSaverExit(void) +{ + if (gScreenSaverDisplayed) { + gScreenSaverDisplayed = false; + gUpdateDisplay = true; + } +} +#endif + +bool APP_IsScreenSaverDisplayed(void) +{ +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + return gScreenSaverDisplayed; +#else + return false; +#endif +} + static void CheckForIncoming(void) { if (!g_SquelchLost) return; // squelch is closed +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + ScreenSaverExit(); +#endif + // squelch is open if (gScanStateDir == SCAN_OFF) @@ -495,6 +614,10 @@ void APP_StartListening(FUNCTION_Type_t function) { const unsigned int vfo = gEeprom.RX_VFO; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + ScreenSaverExit(); +#endif + #ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER gRxTimerCountdown_500ms = 7200; #endif @@ -1414,6 +1537,23 @@ void APP_TimeSlice10ms(void) bool gUpdateDisplayCurrent = gUpdateDisplay; bool gUpdateStatusCurrent = gUpdateStatus; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + if (gScreenSaverDisplayed && gUpdateDisplayCurrent) { + gUpdateDisplayCurrent = false; + gUpdateDisplay = false; + } else if (gScreenSaverDisplayed && gUpdateStatusCurrent) { + gUpdateStatusCurrent = false; + gUpdateStatus = false; + } + + if (gScreenSaverDisplayed && gScreenSaverMode == SET_SAV_MATRIX && !gUpdateDisplayCurrent) { + if (++gScreenSaverTick >= 8u) { + gScreenSaverTick = 0; + ScreenSaverRenderMatrix(false); + } + } +#endif + if (gUpdateDisplayCurrent) { gUpdateDisplay = false; GUI_DisplayScreen(); @@ -1634,6 +1774,20 @@ void APP_TimeSlice500ms(void) && gEeprom.BACKLIGHT_TIME < 61 ) { BACKLIGHT_TurnOff(); +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + if (gSetting_set_sav != SET_SAV_OFF && gScreenToDisplay == DISPLAY_MAIN) { + if (gSetting_set_sav == SET_SAV_LOGO) + UI_DisplayLogo(); + else if (gSetting_set_sav == SET_SAV_MATRIX) + ScreenSaverRenderMatrix(true); + + gScreenSaverDisplayed = true; + gScreenSaverMode = gSetting_set_sav; + gScreenSaverTick = 0; + gUpdateDisplay = false; + gUpdateStatus = false; + } +#endif } #ifdef ENABLE_FEAT_F4HWN_SLEEP @@ -1895,6 +2049,24 @@ static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) } #endif +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + if (gScreenSaverWakeKey != KEY_INVALID) { + if (Key == gScreenSaverWakeKey && !bKeyPressed) + gScreenSaverWakeKey = KEY_INVALID; + return; + } + + if (bKeyPressed && gScreenSaverDisplayed) { + ScreenSaverExit(); + BACKLIGHT_TurnOn(); + if (Key != KEY_PTT) { + gScreenSaverWakeKey = Key; + gBeepToPlay = BEEP_NONE; + return; + } + } +#endif + if (Key == KEY_EXIT && !BACKLIGHT_IsOn() && gEeprom.BACKLIGHT_TIME > 0) { // just turn the light on for now so the user can see what's what BACKLIGHT_TurnOn(); diff --git a/App/app/app.h b/App/app/app.h index 2beb26e1..602dcc1c 100644 --- a/App/app/app.h +++ b/App/app/app.h @@ -31,6 +31,6 @@ uint32_t APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t direction); void APP_Update(void); void APP_TimeSlice10ms(void); void APP_TimeSlice500ms(void); +bool APP_IsScreenSaverDisplayed(void); #endif - diff --git a/App/app/menu.c b/App/app/menu.c index 55c1f7a2..4ce70cef 100644 --- a/App/app/menu.c +++ b/App/app/menu.c @@ -222,6 +222,13 @@ int MENU_GetLimits(uint8_t menu_id, int32_t *pMin, int32_t *pMax) *pMax = ARRAY_SIZE(gSubMenu_RX_TX) - 1; break; +#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV) + case MENU_SET_SAV: + //*pMin = 0; + *pMax = SET_SAV_LEN - 1; + break; +#endif + #ifndef ENABLE_FEAT_F4HWN #ifdef ENABLE_AM_FIX case MENU_AM_FIX: @@ -1030,6 +1037,11 @@ void MENU_AcceptSetting(void) case MENU_SET_TMR: gSetting_set_tmr = gSubMenuSelection; break; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + case MENU_SET_SAV: + gSetting_set_sav = gSubMenuSelection; + break; +#endif case MENU_TX_LOCK: gTxVfo->TX_LOCK = gSubMenuSelection; gRequestSaveChannel = 1; @@ -1497,6 +1509,11 @@ void MENU_ShowCurrentSetting(void) case MENU_SET_TMR: gSubMenuSelection = gSetting_set_tmr; break; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + case MENU_SET_SAV: + gSubMenuSelection = gSetting_set_sav; + break; +#endif case MENU_TX_LOCK: gSubMenuSelection = gTxVfo->TX_LOCK; break; diff --git a/App/misc.c b/App/misc.c index de250535..a68e35db 100644 --- a/App/misc.c +++ b/App/misc.c @@ -137,6 +137,9 @@ enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx; #ifdef ENABLE_FEAT_F4HWN_NARROWER bool gSetting_set_nfm = 0; #endif + #ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + uint8_t gSetting_set_sav = SET_SAV_OFF; + #endif bool gSetting_set_tmr = 0; bool gSetting_set_ptt_session; #ifdef ENABLE_FEAT_F4HWN_DEBUG diff --git a/App/misc.h b/App/misc.h index 688d6e0a..afa5c72e 100644 --- a/App/misc.h +++ b/App/misc.h @@ -181,6 +181,15 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx; #endif #ifdef ENABLE_FEAT_F4HWN + #ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + enum SET_SAV_t { + SET_SAV_OFF, + SET_SAV_LOGO, + SET_SAV_MATRIX, + SET_SAV_LEN + }; + #endif + extern uint8_t gSetting_set_pwr; extern bool gSetting_set_ptt; extern uint8_t gSetting_set_tot; @@ -197,6 +206,9 @@ extern enum BacklightOnRxTx_t gSetting_backlight_on_tx_rx; #ifdef ENABLE_FEAT_F4HWN_NARROWER extern bool gSetting_set_nfm; #endif + #ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + extern uint8_t gSetting_set_sav; + #endif extern bool gSetting_set_tmr; extern bool gSetting_set_ptt_session; #ifdef ENABLE_FEAT_F4HWN_DEBUG diff --git a/App/settings.c b/App/settings.c index 1782f92b..5844ac1b 100644 --- a/App/settings.c +++ b/App/settings.c @@ -454,11 +454,19 @@ gEeprom.FreqChannel[1] = IS_FREQ_CHANNEL(Data16[5]) ? Data16[5] : (FREQ_CHANNE // 1FF0..0x1FF7 // TODO: address TBD PY25Q16_ReadBuffer(0x00A158, Data, 8); - const uint8_t set_ptt_scn = Data[7] & 0x0F; + const uint8_t set_ptt_scn_sav = Data[7] & 0x0F; +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < (SET_SAV_LEN << 2); +#else + const bool set_ptt_scn_sav_valid = set_ptt_scn_sav < 4; +#endif gSetting_set_pwr = (((Data[7] & 0xF0) >> 4) < 7) ? ((Data[7] & 0xF0) >> 4) : 0; - gSetting_set_ptt = (set_ptt_scn < 4) ? (set_ptt_scn & 0x01) : 0; + gSetting_set_ptt = set_ptt_scn_sav_valid ? (set_ptt_scn_sav & 0x01) : 0; #ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER - gSetting_set_scn = (set_ptt_scn < 4) ? ((set_ptt_scn & 0x02) == 0) : 1; + gSetting_set_scn = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav & 0x02) == 0) : 1; +#endif +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + gSetting_set_sav = set_ptt_scn_sav_valid ? ((set_ptt_scn_sav >> 2) & 0x03) : SET_SAV_OFF; #endif gSetting_set_tot = (((Data[6] & 0xF0) >> 4) < 4) ? ((Data[6] & 0xF0) >> 4) : 0; @@ -1131,13 +1139,16 @@ void SETTINGS_SaveSettings(void) State[5] = ((tmp << 4) | (gSetting_set_ctr & 0x0F)); State[6] = ((gSetting_set_tot << 4) | (gSetting_set_eot & 0x0F)); - uint8_t set_ptt_scn = gSetting_set_ptt & 0x01; + uint8_t set_ptt_scn_sav = gSetting_set_ptt & 0x01; #ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER if (!gSetting_set_scn) - set_ptt_scn |= 0x02; + set_ptt_scn_sav |= 0x02; +#endif +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + set_ptt_scn_sav |= (gSetting_set_sav & 0x03) << 2; #endif - State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn); + State[7] = ((gSetting_set_pwr << 4) | set_ptt_scn_sav); gEeprom.KEY_LOCK_PTT = gSetting_set_lck; diff --git a/App/ui/menu.c b/App/ui/menu.c index cb1e5af1..ddd60278 100644 --- a/App/ui/menu.c +++ b/App/ui/menu.c @@ -171,6 +171,9 @@ const t_menu_item MenuList[] = #ifdef ENABLE_FEAT_F4HWN_SCAN_FASTER {"SetScn", MENU_SET_SCN }, #endif +#ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + {"SetSav", MENU_SET_SAV }, +#endif #endif // hidden menu items from here on // enabled if pressing both the PTT and upper side button at power-on @@ -298,6 +301,15 @@ const char* const gSubMenu_PONMSG[] = "NONE" }; +#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV) +const char* const gSubMenu_SET_SAV[] = +{ + "OFF", + "LOGO", + "MATRIX" +}; +#endif + const char* const gSubMenu_ROGER[] = { "OFF", @@ -918,6 +930,12 @@ void UI_DisplayMenu(void) strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]); break; +#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV) + case MENU_SET_SAV: + strcpy(String, gSubMenu_SET_SAV[gSubMenuSelection]); + break; +#endif + case MENU_MEM_CH: case MENU_1_CALL: case MENU_DEL_CH: diff --git a/App/ui/menu.h b/App/ui/menu.h index f6501288..313692cb 100644 --- a/App/ui/menu.h +++ b/App/ui/menu.h @@ -163,7 +163,10 @@ enum MENU_F2SHRT, MENU_F2LONG, MENU_MLONG, - MENU_BATTYP + MENU_BATTYP, +#if defined(ENABLE_FEAT_F4HWN) && defined(ENABLE_FEAT_F4HWN_LOGO_SAV) + MENU_SET_SAV +#endif }; extern const uint8_t FIRST_HIDDEN_MENU_ITEM; @@ -207,6 +210,9 @@ extern const char* const gSubMenu_D_RSP[4]; extern const char* const gSubMenu_SET_AUD_FM[5]; extern const char* const gSubMenu_SET_AUD_AM[3]; #endif + #ifdef ENABLE_FEAT_F4HWN_LOGO_SAV + extern const char* const gSubMenu_SET_SAV[]; + #endif #endif extern const char* const gSubMenu_PTT_ID[5]; diff --git a/App/ui/status.c b/App/ui/status.c index db02caa3..7e9d8f28 100644 --- a/App/ui/status.c +++ b/App/ui/status.c @@ -16,6 +16,7 @@ #include +#include "app/app.h" #include "app/chFrScanner.h" #ifdef ENABLE_FMRADIO #include "app/fm.h" @@ -59,6 +60,9 @@ void UI_DisplayStatus() char str[8] = ""; gUpdateStatus = false; + if (APP_IsScreenSaverDisplayed()) + return; + UI_StatusClear(); uint8_t *line = gStatusLine; diff --git a/App/ui/welcome.c b/App/ui/welcome.c index 4b6298ea..d80d08a4 100644 --- a/App/ui/welcome.c +++ b/App/ui/welcome.c @@ -44,6 +44,21 @@ #define LOGO_FLASH_ADDR 0x011000 #define LOGO_HEADER_SIZE 8 #define LOGO_BITMAP_ADDR (LOGO_FLASH_ADDR + LOGO_HEADER_SIZE) + +static void UI_LoadLogo(void) +{ + // Skip 8-byte header, then read 128x64 bitmap (1024 B): + // page 0 -> gStatusLine, pages 1..7 -> gFrameBuffer. + PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR, gStatusLine, sizeof(gStatusLine)); + PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR + sizeof(gStatusLine), gFrameBuffer, sizeof(gFrameBuffer)); +} + +void UI_DisplayLogo(void) +{ + UI_LoadLogo(); + ST7565_BlitStatusLine(); + ST7565_BlitFullScreen(); +} #endif #ifdef ENABLE_FEAT_F4HWN_QRCODE @@ -240,10 +255,7 @@ void UI_DisplayWelcome(void) #endif #ifdef ENABLE_FEAT_F4HWN_LOGO else if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_LOGO) { - // Skip 8-byte header, then read 128x64 bitmap (1024 B): - // page 0 -> gStatusLine, pages 1..7 -> gFrameBuffer. - PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR, gStatusLine, sizeof(gStatusLine)); - PY25Q16_ReadBuffer(LOGO_BITMAP_ADDR + sizeof(gStatusLine), gFrameBuffer, sizeof(gFrameBuffer)); + UI_LoadLogo(); } #endif else { diff --git a/App/ui/welcome.h b/App/ui/welcome.h index 0cfb26e2..8c8de55c 100644 --- a/App/ui/welcome.h +++ b/App/ui/welcome.h @@ -23,6 +23,10 @@ void UI_DisplayReleaseKeys(void); void UI_DisplayWelcome(void); +#ifdef ENABLE_FEAT_F4HWN_LOGO +void UI_DisplayLogo(void); +#endif + #ifdef ENABLE_FEAT_F4HWN_QRCODE // Draw the embedded GitHub QR code (V4, 33x33) at (origin_x, origin_y). // `wiki` selects the wiki URL bitmap, otherwise the repo URL bitmap. @@ -35,4 +39,3 @@ void UI_GetMemPercents(uint16_t *flash_pct_x100, uint16_t *ram_pct_x100); #endif #endif - diff --git a/CMakePresets.json b/CMakePresets.json index abbeb628..f9d70c9c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -72,6 +72,7 @@ "ENABLE_FEAT_F4HWN_SCAN_FASTER": false, "ENABLE_FEAT_F4HWN_BEAM": false, "ENABLE_FEAT_F4HWN_LOGO": false, + "ENABLE_FEAT_F4HWN_LOGO_SAV": false, "ENABLE_AGC_SHOW_DATA": false, "ENABLE_UART_RW_BK_REGS": false, "ENABLE_SWD": false, @@ -202,6 +203,7 @@ "ENABLE_FEAT_F4HWN_QRCODE": true, "ENABLE_FEAT_F4HWN_MEM": true, "ENABLE_FEAT_F4HWN_LOGO": true, + "ENABLE_FEAT_F4HWN_LOGO_SAV": true, "ENABLE_SWD": true, "EDITION_STRING": "Fusion", "TARGET": "f4hwn.fusion"